def mouseDoubleClickEvent(self, event): if self.kind == "directory": self.double_clicked.emit() else: print("execute =", os.path.join(self.path, self.name)) file = "\"" + os.path.join(self.path, self.name) + "\"" QProcess.execute("/usr/bin/open " + file)
def __startProc(self, applName, *applArgs): """ Private method to start an eric6 application. @param applName name of the eric6 application script (string) @param *applArgs variable list of application arguments """ proc = QProcess() applPath = os.path.join(getConfig("ericDir"), applName) args = [] args.append(applPath) for arg in applArgs: args.append(arg) if not os.path.isfile(applPath) or \ not proc.startDetached(sys.executable, args): E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( '<p>Could not start the process.<br>' 'Ensure that it is available as <b>{0}</b>.</p>' ).format(applPath), self.tr('OK'))
def __init__(self, **kwargs): QObject.__init__(self) self.filename = kwargs.get("filename") self.text_code = kwargs.get("code") self.__python_exec = kwargs.get("python_exec") self.pre_script = kwargs.get("pre_script") self.post_script = kwargs.get("post_script") self.__params = kwargs.get("params") self.__elapsed = QElapsedTimer() self.outputw = None self.__current_process = None self.main_process = QProcess(self) self.main_process.started.connect(self._process_started) self.main_process.finished.connect(self._process_finished) self.main_process.finished.connect(self.__post_execution) self.main_process.readyReadStandardOutput.connect(self._refresh_output) self.main_process.readyReadStandardError.connect(self._refresh_error) self.pre_process = QProcess(self) self.pre_process.started.connect(self._process_started) self.pre_process.finished.connect(self._process_finished) self.pre_process.finished.connect(self.__main_execution) self.pre_process.readyReadStandardOutput.connect(self._refresh_output) self.pre_process.readyReadStandardError.connect(self._refresh_error) self.post_process = QProcess(self) self.post_process.started.connect(self._process_started) self.post_process.finished.connect(self._process_finished) self.post_process.readyReadStandardOutput.connect(self._refresh_output) self.post_process.readyReadStandardError.connect(self._refresh_error)
def __init__(self): super(RunWidget, self).__init__() vbox = QVBoxLayout(self) vbox.setSpacing(0) vbox.setContentsMargins(0, 0, 0, 0) self.output = OutputWidget(self) hbox = QHBoxLayout() self.input = QLineEdit() self.lblInput = QLabel(_translate("RunWidget", "Input:")) vbox.addWidget(self.output) hbox.addWidget(self.lblInput) hbox.addWidget(self.input) vbox.addLayout(hbox) self.set_font(settings.FONT_FAMILY, settings.FONT_SIZE) #process self.currentProcess = None self.__preScriptExecuted = False self._proc = QProcess(self) self._preExecScriptProc = QProcess(self) self._postExecScriptProc = QProcess(self) self._proc.readyReadStandardOutput.connect(self.output._refresh_output) self._proc.readyReadStandardError.connect(self.output._refresh_error) self._proc.finished[int, 'QProcess::ExitStatus'].connect(self.finish_execution) self._proc.error['QProcess::ProcessError'].connect(self.process_error) self.input.returnPressed.connect(self.insert_input) self._preExecScriptProc.finished[int, 'QProcess::ExitStatus'].connect(self.__main_execution) self._preExecScriptProc.readyReadStandardOutput.connect(self.output._refresh_output) self._preExecScriptProc.readyReadStandardError.connect(self.output._refresh_error) self._postExecScriptProc.finished[int, 'QProcess::ExitStatus'].connect(self.__post_execution_message) self._postExecScriptProc.readyReadStandardOutput.connect(self.output._refresh_output) self._postExecScriptProc.readyReadStandardError.connect(self.output._refresh_error)
def _createTask(self, path): proc = QProcess(self) proc.input = path proc.readyReadStandardOutput.connect(self.hasOutput) proc.finished.connect(self.finished) proc.start('python', ['thumbnail.py', path]) self.running += 1
def __init__(self): #Call base class method QProcess.__init__(self) self.setProcessChannelMode(QProcess.MergedChannels) self.data = [] self.final_data = None self.readyReadStandardOutput.connect(self.readStdOutput) self.finished.connect(self.killProcess)
def __hgGetShelveNamesList(self, repodir): """ Private method to get the list of shelved changes. @param repodir directory name of the repository (string) @return list of shelved changes (list of string) """ args = self.vcs.initCommand("shelve") args.append('--list') args.append('--quiet') client = self.vcs.getClient() output = "" if client: output = client.runcommand(args)[0] else: process = QProcess() process.setWorkingDirectory(repodir) process.start('hg', args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: output = str(process.readAllStandardOutput(), self.vcs.getEncoding(), 'replace') shelveNamesList = [] for line in output.splitlines(): shelveNamesList.append(line.strip()) return shelveNamesList[:]
def __getCommitMessage(self, repodir): """ Private method to get the commit message of the current patch. @param repodir directory name of the repository (string) @return name of the current patch (string) """ message = "" args = self.vcs.initCommand("qheader") client = self.vcs.getClient() if client: message = client.runcommand(args)[0] else: process = QProcess() process.setWorkingDirectory(repodir) process.start('hg', args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: message = str(process.readAllStandardOutput(), self.vcs.getEncoding(), 'replace') return message
def __getEntries(self, repodir, all): """ Private method to get a list of files/directories being purged. @param repodir directory name of the repository (string) @param all flag indicating to delete all files including ignored ones (boolean) @return name of the current patch (string) """ purgeEntries = [] args = self.vcs.initCommand("purge") args.append("--print") if all: args.append("--all") client = self.vcs.getClient() if client: out, err = client.runcommand(args) if out: purgeEntries = out.strip().split() else: process = QProcess() process.setWorkingDirectory(repodir) process.start('hg', args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: purgeEntries = str( process.readAllStandardOutput(), self.vcs.getEncoding(), 'replace').strip().split() return purgeEntries
def __getParents(self, rev): """ Private method to get the parents of the currently viewed file/directory. @param rev revision number to get parents for (string) @return list of parent revisions (list of strings) """ errMsg = "" parents = [] if int(rev) > 0: args = self.vcs.initCommand("parents") if self.mode == "incoming": if self.bundle: args.append("--repository") args.append(self.bundle) elif self.vcs.bundleFile and \ os.path.exists(self.vcs.bundleFile): args.append("--repository") args.append(self.vcs.bundleFile) args.append("--template") args.append("{rev}:{node|short}\n") args.append("-r") args.append(rev) if not self.projectMode: args.append(self.filename) output = "" if self.__hgClient: output, errMsg = self.__hgClient.runcommand(args) else: process = QProcess() process.setWorkingDirectory(self.repodir) process.start('hg', args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: output = str(process.readAllStandardOutput(), self.vcs.getEncoding(), 'replace') else: if not finished: errMsg = self.tr( "The hg process did not finish within 30s.") else: errMsg = self.tr("Could not start the hg executable.") if errMsg: E5MessageBox.critical( self, self.tr("Mercurial Error"), errMsg) if output: parents = [p for p in output.strip().splitlines()] return parents
def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: if self.parent.file_field.name: file = "\"" + self.parent.file_field.path file += "/" + self.parent.file_field.name + "\"" print(file) QProcess.execute("/usr/bin/open " + file) else: path = self.parent.current_path print(path) QProcess.startDetached( "/Users/freeaks/source/filer/data/filer.py -p " + path) sys.exit(0)
def start(self, path): """ Public slot to start the list command. @param path name of directory to be listed (string) """ dname, fname = self.vcs.splitPath(path) # find the root of the repo repodir = dname while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return args = self.vcs.initCommand("qguard") args.append("--list") output = "" if self.__hgClient: output = self.__hgClient.runcommand(args)[0] else: process = QProcess() process.setWorkingDirectory(repodir) process.start('hg', args) procStarted = process.waitForStarted(5000) if procStarted: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: output = str(process.readAllStandardOutput(), self.vcs.getEncoding(), 'replace') if output: guardsDict = {} for line in output.splitlines(): if line: patchName, guards = line.strip().split(":", 1) guardsDict[patchName] = guards.strip().split() for patchName in sorted(guardsDict.keys()): patchItm = QTreeWidgetItem(self.guardsTree, [patchName]) patchItm.setExpanded(True) for guard in guardsDict[patchName]: if guard.startswith("+"): icon = UI.PixmapCache.getIcon("plus.png") guard = guard[1:] elif guard.startswith("-"): icon = UI.PixmapCache.getIcon("minus.png") guard = guard[1:] else: icon = None guard = self.tr("Unguarded") itm = QTreeWidgetItem(patchItm, [guard]) if icon: itm.setIcon(0, icon) else: QTreeWidgetItem(self.guardsTree, [self.tr("no patches found")])
def launchQml(self, name): import_path = self.resolveDataDir(name) qml = self.resolveQmlFile(name) process = QProcess(self) process.error.connect(self.launchError) env = QProcessEnvironment.systemEnvironment() env.insert('QML2_IMPORT_PATH', import_path) process.setProcessEnvironment(env) executable = QLibraryInfo.location(QLibraryInfo.BinariesPath) + '/qmlscene' Colors.debug("Launching:", executable) process.start(executable, [qml])
def optionsAccepted(self, id): if id == self.otherId: #MouseWidget self.page(1).execute() #ThemeWidget self.page(2).execute() #MenuWidget self.page(3).execute() #WallpaperWidget self.page(4).execute() #AvatarWidget self.page(5).execute() proc1 = QProcess() proc2 = QProcess() proc1.startDetached("killall plasmashell") proc2.waitForStarted(1000) proc2.startDetached("kstart5 plasmashell") if id == self.sumId: self.setButtonText(QWizard.NextButton, self.tr("Apply Settings")) self.summaryVisible.emit() else: self.setButtonText(QWizard.NextButton, self.tr("Next"))
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnTagBranchListDialog, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.vcs = vcs self.tagsList = None self.allTagsList = None self.tagList.headerItem().setText(self.tagList.columnCount(), "") self.tagList.header().setSortIndicator(3, Qt.AscendingOrder) self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_list = QRegExp( r"""\w*\s*(\d+)\s+(\w+)\s+\d*\s*""" r"""((?:\w+\s+\d+|[0-9.]+\s+\w+)\s+[0-9:]+)\s+(.+)/\s*""")
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(HgAnnotateDialog, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.vcs = vcs self.__hgClient = vcs.getClient() self.__annotateRe = re.compile( r"""(.+)\s+(\d+)\s+([0-9a-fA-F]+)\s+([0-9-]+)\s+(.+)""") self.annotateList.headerItem().setText( self.annotateList.columnCount(), "") font = Preferences.getEditorOtherFonts("MonospacedFont") self.annotateList.setFont(font) if self.__hgClient: self.process = None else: self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.show() QCoreApplication.processEvents()
def run_emulator(self): self.proc = QProcess(self) self.proc.started.connect(self.timer.start) self.proc.finished.connect(self.exit_app) self.proc.error.connect(self.on_error) self.proc.setProcessChannelMode(QProcess.ForwardedChannels) self.proc.start('fs-uae', ['--lua_shell=1'] + sys.argv[1:])
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnBlameDialog, self).__init__(parent) self.setupUi(self) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.process = QProcess() self.vcs = vcs self.blameList.headerItem().setText(self.blameList.columnCount(), "") font = Preferences.getEditorOtherFonts("MonospacedFont") self.blameList.setFont(font) self.__ioEncoding = Preferences.getSystem("IOEncoding") self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr)
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnLogBrowserDialog, self).__init__(parent) self.setupUi(self) self.__position = QPoint() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") self.filesTree.header().setSortIndicator(0, Qt.AscendingOrder) self.vcs = vcs self.__initData() self.fromDate.setDisplayFormat("yyyy-MM-dd") self.toDate.setDisplayFormat("yyyy-MM-dd") self.__resetUI() self.__messageRole = Qt.UserRole self.__changesRole = Qt.UserRole + 1 self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_sep1 = QRegExp('\\-+\\s*') self.rx_sep2 = QRegExp('=+\\s*') self.rx_rev1 = QRegExp( 'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "rev" followed by one or more decimals followed by a colon followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_rev2 = QRegExp( 'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "r" followed by one or more decimals followed by " | " followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_flags1 = QRegExp( r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") # three blanks followed by A or D or M followed by path followed by # path copied from followed by copied from revision self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') # three blanks followed by A or D or M followed by path self.flags = { 'A': self.tr('Added'), 'D': self.tr('Deleted'), 'M': self.tr('Modified'), 'R': self.tr('Replaced'), } self.intercept = False
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnDiffDialog, self).__init__(parent) self.setupUi(self) self.refreshButton = self.buttonBox.addButton( self.tr("Refresh"), QDialogButtonBox.ActionRole) self.refreshButton.setToolTip( self.tr("Press to refresh the display")) self.refreshButton.setEnabled(False) self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.searchWidget.attachTextEdit(self.contents) self.process = QProcess() self.vcs = vcs font = Preferences.getEditorOtherFonts("MonospacedFont") self.contents.setFontFamily(font.family()) self.contents.setFontPointSize(font.pointSize()) self.highlighter = SvnDiffHighlighter(self.contents.document()) self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr)
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent reference to the parent widget (QWidget) """ super(HgQueuesHeaderDialog, self).__init__(parent) self.setupUi(self) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.vcs = vcs self.__hgClient = vcs.getClient() if self.__hgClient: self.process = None else: self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.show() QCoreApplication.processEvents()
def edit(self, text): """Edit a given text. Args: text: The initial text to edit. """ if self._text is not None: raise ValueError("Already editing a file!") self._text = text try: self._oshandle, self._filename = tempfile.mkstemp(text=True) if text: encoding = config.get('general', 'editor-encoding') with open(self._filename, 'w', encoding=encoding) as f: f.write(text) except OSError as e: message.error(self._win_id, "Failed to create initial file: " "{}".format(e)) return self._proc = QProcess(self) self._proc.finished.connect(self.on_proc_closed) self._proc.error.connect(self.on_proc_error) editor = config.get('general', 'editor') executable = editor[0] args = [self._filename if arg == '{}' else arg for arg in editor[1:]] log.procs.debug("Calling \"{}\" with args {}".format(executable, args)) self._proc.start(executable, args)
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(HgBookmarksListDialog, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.refreshButton = self.buttonBox.addButton( self.tr("Refresh"), QDialogButtonBox.ActionRole) self.refreshButton.setToolTip( self.tr("Press to refresh the bookmarks display")) self.refreshButton.setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.process = QProcess() self.vcs = vcs self.__bookmarksList = None self.__path = None self.__hgClient = vcs.getClient() self.bookmarksList.headerItem().setText( self.bookmarksList.columnCount(), "") self.bookmarksList.header().setSortIndicator(3, Qt.AscendingOrder) self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.show() QCoreApplication.processEvents()
def __init__(self, parent=None): super().__init__(parent) assert self.PROCESS_NAME is not None self._invalid = False self._data = [] self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardError)
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnPropListDialog, self).__init__(parent) self.setupUi(self) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.process = QProcess() env = QProcessEnvironment.systemEnvironment() env.insert("LANG", "C") self.process.setProcessEnvironment(env) self.vcs = vcs self.propsList.headerItem().setText(self.propsList.columnCount(), "") self.propsList.header().setSortIndicator(0, Qt.AscendingOrder) self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_path = QRegExp(r"Properties on '([^']+)':\s*") self.rx_prop = QRegExp(r" (.*) *: *(.*)[\r\n]") self.lastPath = None self.lastProp = None self.propBuffer = ""
def __init__(self, parent=None): super().__init__(parent) self.captured_log = [] self._invalid = [] self._data = [] self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardError)
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(HgTagBranchListDialog, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.vcs = vcs self.tagsList = None self.allTagsList = None self.__hgClient = vcs.getClient() self.tagList.headerItem().setText(self.tagList.columnCount(), "") self.tagList.header().setSortIndicator(3, Qt.AscendingOrder) if self.__hgClient: self.process = None else: self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.show() QCoreApplication.processEvents()
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(HgDiffDialog, self).__init__(parent) self.setupUi(self) self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.process = QProcess() self.vcs = vcs self.__hgClient = self.vcs.getClient() font = Preferences.getEditorOtherFonts("MonospacedFont") self.contents.setFontFamily(font.family()) self.contents.setFontPointSize(font.pointSize()) self.cNormalFormat = self.contents.currentCharFormat() self.cAddedFormat = self.contents.currentCharFormat() self.cAddedFormat.setBackground(QBrush(QColor(190, 237, 190))) self.cRemovedFormat = self.contents.currentCharFormat() self.cRemovedFormat.setBackground(QBrush(QColor(237, 190, 190))) self.cLineNoFormat = self.contents.currentCharFormat() self.cLineNoFormat.setBackground(QBrush(QColor(255, 220, 168))) self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr)
def __init__(self, parent=None): super().__init__(parent) self._invalid = False self._requests = [] self.port = self._get_port() self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardError)
def startServer(self): """ Public method to start the command server. @return tuple of flag indicating a successful start (boolean) and an error message (string) in case of failure """ self.__server = QProcess() self.__server.setWorkingDirectory(self.__repoPath) # connect signals self.__server.finished.connect(self.__serverFinished) prepareProcess(self.__server, self.__encoding) self.__server.start('hg', self.__serverArgs) serverStarted = self.__server.waitForStarted(5000) if not serverStarted: return False, self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.' ).format('hg') self.__server.setReadChannel(QProcess.StandardOutput) ok, error = self.__readHello() self.__started = ok return ok, error
class VideoPlayer(QWidget): def __init__(self, aPath, parent=None): super(VideoPlayer, self).__init__(parent) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAcceptDrops(True) self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.StreamPlayback) self.mediaPlayer.mediaStatusChanged.connect(self.printMediaData) self.mediaPlayer.setVolume(80) self.videoWidget = QVideoWidget(self) self.lbl = QLineEdit('00:00:00') self.lbl.setReadOnly(True) self.lbl.setFixedWidth(70) self.lbl.setUpdatesEnabled(True) self.lbl.setStyleSheet(stylesheet(self)) self.elbl = QLineEdit('00:00:00') self.elbl.setReadOnly(True) self.elbl.setFixedWidth(70) self.elbl.setUpdatesEnabled(True) self.elbl.setStyleSheet(stylesheet(self)) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setFixedWidth(32) self.playButton.setStyleSheet("background-color: black") self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal, self) self.positionSlider.setStyleSheet(stylesheet(self)) self.positionSlider.setRange(0, 100) self.positionSlider.sliderMoved.connect(self.setPosition) self.positionSlider.sliderMoved.connect(self.handleLabel) self.positionSlider.setSingleStep(2) self.positionSlider.setPageStep(20) self.positionSlider.setAttribute(Qt.WA_TranslucentBackground, True) self.clip = QApplication.clipboard() self.process = QProcess(self) self.process.readyRead.connect(self.dataReady) # self.process.started.connect(lambda: print("grabbing YouTube URL")) self.process.finished.connect(self.playFromURL) self.myurl = "" controlLayout = QHBoxLayout() controlLayout.setContentsMargins(5, 0, 5, 0) controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.lbl) controlLayout.addWidget(self.positionSlider) controlLayout.addWidget(self.elbl) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.videoWidget) layout.addLayout(controlLayout) self.setLayout(layout) self.myinfo = "©2016\nAxel Schneider\n\nMouse Wheel = Zoom\nUP = Volume Up\nDOWN = Volume Down\n" + \ "LEFT = < 1 Minute\nRIGHT = > 1 Minute\n" + \ "SHIFT+LEFT = < 10 Minutes\nSHIFT+RIGHT = > 10 Minutes" self.widescreen = True #### shortcuts #### self.shortcut = QShortcut(QKeySequence("q"), self) self.shortcut.activated.connect(self.handleQuit) self.shortcut = QShortcut(QKeySequence("u"), self) self.shortcut.activated.connect(self.playFromURL) self.shortcut = QShortcut(QKeySequence("y"), self) self.shortcut.activated.connect(self.getYTUrl) self.shortcut = QShortcut(QKeySequence("o"), self) self.shortcut.activated.connect(self.openFile) self.shortcut = QShortcut(QKeySequence(" "), self) self.shortcut.activated.connect(self.play) self.shortcut = QShortcut(QKeySequence("f"), self) self.shortcut.activated.connect(self.handleFullscreen) self.shortcut = QShortcut(QKeySequence("i"), self) self.shortcut.activated.connect(self.handleInfo) self.shortcut = QShortcut(QKeySequence("s"), self) self.shortcut.activated.connect(self.toggleSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Right), self) self.shortcut.activated.connect(self.forwardSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Left), self) self.shortcut.activated.connect(self.backSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Up), self) self.shortcut.activated.connect(self.volumeUp) self.shortcut = QShortcut(QKeySequence(Qt.Key_Down), self) self.shortcut.activated.connect(self.volumeDown) self.shortcut = QShortcut( QKeySequence(Qt.ShiftModifier + Qt.Key_Right), self) self.shortcut.activated.connect(self.forwardSlider10) self.shortcut = QShortcut(QKeySequence(Qt.ShiftModifier + Qt.Key_Left), self) self.shortcut.activated.connect(self.backSlider10) self.mediaPlayer.setVideoOutput(self.videoWidget) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.positionChanged.connect(self.handleLabel) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.error.connect(self.handleError) print("QT5 Player started") self.suspend_screensaver() def playFromURL(self): self.mediaPlayer.pause() self.myurl = self.clip.text() self.mediaPlayer.setMedia(QMediaContent(QUrl(self.myurl))) self.playButton.setEnabled(True) self.mediaPlayer.play() self.hideSlider() print(self.myurl) def getYTUrl(self): cmd = "youtube-dl -g -f best " + self.clip.text() print("grabbing YouTube URL") self.process.start(cmd) def dataReady(self): self.myurl = str(self.process.readAll(), encoding='utf8').rstrip() ### self.myurl = self.myurl.partition("\n")[0] print(self.myurl) self.clip.setText(self.myurl) self.playFromURL() def suspend_screensaver(self): 'suspend linux screensaver' proc = subprocess.Popen( 'gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', shell=True) proc.wait() def resume_screensaver(self): 'resume linux screensaver' proc = subprocess.Popen( 'gsettings set org.gnome.desktop.screensaver idle-activation-enabled true', shell=True) proc.wait() def openFile(self): fileName, _ = QFileDialog.getOpenFileName( self, "Open Movie", QDir.homePath() + "/Videos", "Media (*.webm *.mp4 *.ts *.avi *.mpeg *.mpg *.mkv *.VOB *.m4v *.3gp *.mp3 *.m4a *.wav *.ogg *.flac *.m3u *.m3u8)" ) if fileName != '': self.loadFilm(fileName) print("File loaded") def play(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPause)) else: self.playButton.setIcon(self.style().standardIcon( QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) mtime = QTime(0, 0, 0, 0) mtime = mtime.addMSecs(self.mediaPlayer.duration()) self.elbl.setText(mtime.toString()) def setPosition(self, position): self.mediaPlayer.setPosition(position) def handleError(self): self.playButton.setEnabled(False) print("Error: ", self.mediaPlayer.errorString()) def handleQuit(self): self.mediaPlayer.stop() self.resume_screensaver() print("Goodbye ...") app.quit() def contextMenuRequested(self, point): menu = QMenu() actionFile = menu.addAction(QIcon.fromTheme("video-x-generic"), "open File (o)") actionclipboard = menu.addSeparator() actionURL = menu.addAction(QIcon.fromTheme("browser"), "URL from Clipboard (u)") actionclipboard = menu.addSeparator() actionYTurl = menu.addAction(QIcon.fromTheme("youtube"), "URL from YouTube (y)") actionclipboard = menu.addSeparator() actionToggle = menu.addAction(QIcon.fromTheme("next"), "show / hide Slider (s)") actionFull = menu.addAction(QIcon.fromTheme("view-fullscreen"), "Fullscreen (f)") action169 = menu.addAction(QIcon.fromTheme("tv-symbolic"), "16 : 9") action43 = menu.addAction(QIcon.fromTheme("tv-symbolic"), "4 : 3") actionSep = menu.addSeparator() actionInfo = menu.addAction(QIcon.fromTheme("help-about"), "Info (i)") action5 = menu.addSeparator() actionQuit = menu.addAction(QIcon.fromTheme("application-exit"), "Exit (q)") actionFile.triggered.connect(self.openFile) actionQuit.triggered.connect(self.handleQuit) actionFull.triggered.connect(self.handleFullscreen) actionInfo.triggered.connect(self.handleInfo) actionToggle.triggered.connect(self.toggleSlider) actionURL.triggered.connect(self.playFromURL) actionYTurl.triggered.connect(self.getYTUrl) action169.triggered.connect(self.screen169) action43.triggered.connect(self.screen43) menu.exec_(self.mapToGlobal(point)) def wheelEvent(self, event): mwidth = self.frameGeometry().width() mheight = self.frameGeometry().height() mleft = self.frameGeometry().left() mtop = self.frameGeometry().top() mscale = event.angleDelta().y() / 5 if self.widescreen == True: self.setGeometry(mleft, mtop, mwidth + mscale, (mwidth + mscale) / 1.778) else: self.setGeometry(mleft, mtop, mwidth + mscale, (mwidth + mscale) / 1.33) def screen169(self): self.widescreen = True mwidth = self.frameGeometry().width() mheight = self.frameGeometry().height() mleft = self.frameGeometry().left() mtop = self.frameGeometry().top() mratio = 1.778 self.setGeometry(mleft, mtop, mwidth, mwidth / mratio) def screen43(self): self.widescreen = False mwidth = self.frameGeometry().width() mheight = self.frameGeometry().height() mleft = self.frameGeometry().left() mtop = self.frameGeometry().top() mratio = 1.33 self.setGeometry(mleft, mtop, mwidth, mwidth / mratio) def handleFullscreen(self): if self.windowState() & Qt.WindowFullScreen: self.showNormal() print("no Fullscreen") else: self.showFullScreen() print("Fullscreen entered") def handleInfo(self): msg = QMessageBox.about(self, "QT5 Player", self.myinfo) def toggleSlider(self): if self.positionSlider.isVisible(): self.hideSlider() else: self.showSlider() def hideSlider(self): self.playButton.hide() self.lbl.hide() self.positionSlider.hide() self.elbl.hide() mwidth = self.frameGeometry().width() mheight = self.frameGeometry().height() mleft = self.frameGeometry().left() mtop = self.frameGeometry().top() if self.widescreen == True: self.setGeometry(mleft, mtop, mwidth, mwidth / 1.778) else: self.setGeometry(mleft, mtop, mwidth, mwidth / 1.33) def showSlider(self): self.playButton.show() self.lbl.show() self.positionSlider.show() self.elbl.show() mwidth = self.frameGeometry().width() mheight = self.frameGeometry().height() mleft = self.frameGeometry().left() mtop = self.frameGeometry().top() if self.widescreen == True: self.setGeometry(mleft, mtop, mwidth, mwidth / 1.55) else: self.setGeometry(mleft, mtop, mwidth, mwidth / 1.33) def forwardSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 1000 * 60) def forwardSlider10(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 10000 * 60) def backSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 1000 * 60) def backSlider10(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 10000 * 60) def volumeUp(self): self.mediaPlayer.setVolume(self.mediaPlayer.volume() + 10) print("Volume: " + str(self.mediaPlayer.volume())) def volumeDown(self): self.mediaPlayer.setVolume(self.mediaPlayer.volume() - 10) print("Volume: " + str(self.mediaPlayer.volume())) def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton: self.move(event.globalPos() \ - QPoint(self.frameGeometry().width() / 2, \ self.frameGeometry().height() / 2)) event.accept() def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() elif event.mimeData().hasText(): event.accept() else: event.ignore() def dropEvent(self, event): if event.mimeData().hasUrls(): f = str(event.mimeData().urls()[0].toLocalFile()) self.loadFilm(f) elif event.mimeData().hasText(): mydrop = str(event.mimeData().text()) print(mydrop) ### YouTube url if "https://www.youtube" in mydrop: print("is YouTube") # mydrop = mydrop.partition("&")[0].replace("watch?v=", "v/") self.clip.setText(mydrop) self.getYTUrl() else: ### normal url self.mediaPlayer.setMedia(QMediaContent(QUrl(mydrop))) self.playButton.setEnabled(True) self.mediaPlayer.play() self.hideSlider() print(mydrop) def loadFilm(self, f): self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(f))) self.playButton.setEnabled(True) self.mediaPlayer.play() def printMediaData(self): if self.mediaPlayer.mediaStatus() == 6: if self.mediaPlayer.isMetaDataAvailable(): res = str(self.mediaPlayer.metaData("Resolution")).partition( "PyQt5.QtCore.QSize(")[2].replace(", ", " x ").replace(")", "") print("%s%s" % ("Video Resolution = ", res)) else: print("no metaData available") def openFileAtStart(self, filelist): matching = [s for s in filelist if ".myformat" in s] if len(matching) > 0: self.loadFilm(matching) ##################### update Label ################################## def handleLabel(self): self.lbl.clear() mtime = QTime(0, 0, 0, 0) self.time = mtime.addMSecs(self.mediaPlayer.position()) self.lbl.setText(self.time.toString())
def resetAll(self): self.settings.reset() self.close() proc = QProcess() proc.startDetached(os.path.abspath(__file__))
def run(self): mytask = QProcess() mytask.execute(self.proc, self.args)
def __compileUI(self, fn, noDialog=False, progress=None): """ Private method to compile a .ui file to a .py/.rb file. @param fn filename of the .ui file to be compiled @param noDialog flag indicating silent operations @param progress reference to the progress dialog @return reference to the compile process (QProcess) """ self.compileProc = QProcess() args = [] self.buf = "" if self.project.pdata["PROGLANGUAGE"][0] in \ ["Python", "Python2", "Python3"]: if self.project.getProjectType() in ["Qt4", "E4Plugin"]: self.uicompiler = 'pyuic4' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.bat' else: uic = self.uicompiler elif self.project.getProjectType() in ["PyQt5", "E6Plugin"]: self.uicompiler = 'pyuic5' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.bat' else: uic = self.uicompiler elif self.project.getProjectType() == "PySide": self.uicompiler = 'pyside-uic' uic = Utilities.generatePySideToolPath(self.uicompiler) else: return None elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby": if self.project.getProjectType() == "Qt4": self.uicompiler = 'rbuic4' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.exe' else: uic = self.uicompiler else: return None else: return None ofn, ext = os.path.splitext(fn) fn = os.path.join(self.project.ppath, fn) if self.project.pdata["PROGLANGUAGE"][0] in \ ["Python", "Python2", "Python3"]: dirname, filename = os.path.split(ofn) self.compiledFile = os.path.join(dirname, "Ui_" + filename + ".py") args.append("-x") indentWidth = Preferences.getQt("PyuicIndent") if indentWidth != self.PyuicIndentDefault: args.append("--indent={0}".format(indentWidth)) if Preferences.getQt("PyuicFromImports"): args.append("--from-imports") elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby": self.compiledFile = ofn + '.rb' args.append('-x') args.append(fn) self.compileProc.finished.connect(self.__compileUIDone) self.compileProc.readyReadStandardOutput.connect(self.__readStdout) self.compileProc.readyReadStandardError.connect(self.__readStderr) self.noDialog = noDialog self.compileProc.start(uic, args) procStarted = self.compileProc.waitForStarted(5000) if procStarted: self.compileRunning = True e5App().getObject("ViewManager").enableEditorsCheckFocusIn(False) return self.compileProc else: self.compileRunning = False if progress is not None: progress.cancel() E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'Could not start {0}.<br>' 'Ensure that it is in the search path.' ).format(self.uicompiler)) return None
class ExternalEditor(QObject): """Class to simplify editing a text in an external editor. Attributes: _text: The current text before the editor is opened. _oshandle: The OS level handle to the tmpfile. _filehandle: The file handle to the tmpfile. _proc: The QProcess of the editor. _win_id: The window ID the ExternalEditor is associated with. """ editing_finished = pyqtSignal(str) def __init__(self, win_id, parent=None): super().__init__(parent) self._text = None self._oshandle = None self._filename = None self._proc = None self._win_id = win_id def _cleanup(self): """Clean up temporary files after the editor closed.""" try: os.close(self._oshandle) os.remove(self._filename) except OSError as e: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error(self._win_id, "Failed to delete tempfile... ({})".format(e)) def on_proc_closed(self, exitcode, exitstatus): """Write the editor text into the form field and clean up tempfile. Callback for QProcess when the editor was closed. """ log.procs.debug("Editor closed") if exitstatus != QProcess.NormalExit: # No error/cleanup here, since we already handle this in # on_proc_error return try: if exitcode != 0: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error( self._win_id, "Editor did quit abnormally (status " "{})!".format(exitcode)) return encoding = config.get('general', 'editor-encoding') try: with open(self._filename, 'r', encoding=encoding) as f: text = ''.join(f.readlines()) except OSError as e: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error( self._win_id, "Failed to read back edited file: " "{}".format(e)) return log.procs.debug("Read back: {}".format(text)) self.editing_finished.emit(text) finally: self._cleanup() def on_proc_error(self, error): """Display an error message and clean up when editor crashed.""" messages = { QProcess.FailedToStart: "The process failed to start.", QProcess.Crashed: "The process crashed.", QProcess.Timedout: "The last waitFor...() function timed out.", QProcess.WriteError: ("An error occurred when attempting to write " "to the process."), QProcess.ReadError: ("An error occurred when attempting to read " "from the process."), QProcess.UnknownError: "An unknown error occurred.", } # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error(self._win_id, "Error while calling editor: {}".format(messages[error])) self._cleanup() def edit(self, text): """Edit a given text. Args: text: The initial text to edit. """ if self._text is not None: raise ValueError("Already editing a file!") self._text = text try: self._oshandle, self._filename = tempfile.mkstemp(text=True) if text: encoding = config.get('general', 'editor-encoding') with open(self._filename, 'w', encoding=encoding) as f: f.write(text) except OSError as e: message.error(self._win_id, "Failed to create initial file: " "{}".format(e)) return self._proc = QProcess(self) self._proc.finished.connect(self.on_proc_closed) self._proc.error.connect(self.on_proc_error) editor = config.get('general', 'editor') executable = editor[0] args = [self._filename if arg == '{}' else arg for arg in editor[1:]] log.procs.debug("Calling \"{}\" with args {}".format(executable, args)) self._proc.start(executable, args)
class ConfigUi(QWidget): check_finish_signal = pyqtSignal() def __init__(self): super(ConfigUi, self).__init__() self.__line_ts_token = QLineEdit() self.__line_nosql_db_host = QLineEdit('localhost') self.__line_nosql_db_port = QLineEdit('27017') self.__line_nosql_db_user = QLineEdit() self.__line_nosql_db_pass = QLineEdit() self.__line_mongo_db_binary = QLineEdit( 'C:\\Program Files\\MongoDB\Server\\4.0\\bin') self.__button_browse = QPushButton('Browse') self.__button_import = QPushButton('Import') self.__button_export = QPushButton('Export') self.__combo_web_proxy_protocol = QComboBox() self.__line_web_proxy_host = QLineEdit('') self.__button_ok = QPushButton('OK') self.__button_exit = QPushButton('Exit') self.__text_information = QTextEdit() # self.__label_information = QLabel() # Command self.__process = None self.__pending_command = [] self.init_ui() # ---------------------------------------------------- UI Init ----------------------------------------------------- def init_ui(self): self.__layout_control() self.__config_control() def __layout_control(self): main_layout = QGridLayout() self.setLayout(main_layout) self.setMinimumSize(600, 400) main_layout.addWidget(QLabel('NoSql Host: '), 0, 0) main_layout.addWidget(self.__line_nosql_db_host, 0, 1) main_layout.addWidget(QLabel('NoSql Port: '), 0, 2) main_layout.addWidget(self.__line_nosql_db_port, 0, 3) main_layout.addWidget(QLabel('NoSql User: '******'NoSql Pass: '******'Ts Token: '), 2, 0) main_layout.addWidget(self.__line_ts_token, 2, 1, 1, 3) main_layout.addWidget(QLabel('MongoDB bin: '), 3, 0) mongodb_area = QHBoxLayout() mongodb_area.addWidget(self.__line_mongo_db_binary) mongodb_area.addWidget(self.__button_browse) mongodb_area.addWidget(self.__button_export) mongodb_area.addWidget(self.__button_import) main_layout.addLayout(mongodb_area, 3, 1, 1, 3) main_layout.addWidget(QLabel('Internet Proxy: '), 4, 0) main_layout.addWidget(self.__combo_web_proxy_protocol, 4, 1) main_layout.addWidget(self.__line_web_proxy_host, 4, 2, 1, 2) main_layout.addWidget(QLabel('Status: '), 5, 0) button_area = QHBoxLayout() button_area.addWidget(self.__button_ok) button_area.addWidget(self.__button_exit) main_layout.addLayout(button_area, 5, 3) main_layout.addWidget(self.__text_information, 6, 0, 1, 4) def __config_control(self): self.setWindowTitle('System Config') self.__button_ok.clicked.connect(self.on_button_ok) self.__button_exit.clicked.connect(self.on_button_exit) self.__button_browse.clicked.connect(self.on_button_browse) self.__button_import.clicked.connect(self.on_button_import) self.__button_export.clicked.connect(self.on_button_export) # self.__text_information.setEnabled(False) self.__text_information.setStyleSheet( "QLabel{border:2px solid rgb(0, 0, 0);}") self.__text_information.setTextInteractionFlags( Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) self.__combo_web_proxy_protocol.setEditable(True) self.__combo_web_proxy_protocol.addItem('HTTP_PROXY') self.__combo_web_proxy_protocol.addItem('HTTPS_PROXY') sas = StockAnalysisSystem() logs = sas.get_log_errors() self.__config_to_ui() self.__text_information.setText('\n'.join(logs)) def on_button_ok(self): self.__ui_to_config() self.close() def on_button_exit(self): sys.exit(0) def on_button_browse(self): folder = str( QFileDialog.getExistingDirectory( self, "Select MongoDB Binary Directory", directory=self.__line_mongo_db_binary.text())) if folder == '': return self.__line_mongo_db_binary.setText(folder) def on_button_import(self): if not self.__check_alarm_mongodb_config(): return folder = str( QFileDialog.getExistingDirectory(self, "Select MongoDB Data Directory", directory=path.dirname( path.abspath(__file__)))) if folder == '': return folder_name = path.basename(path.normpath(folder)) if folder_name not in ['StockAnalysisSystem', 'StockDaily']: QMessageBox.information( self, QtCore.QCoreApplication.translate('main', '错误的数据'), QtCore.QCoreApplication.translate( 'main', '数据必须为StockAnalysisSystem或StockDaily之一'), QMessageBox.Ok, QMessageBox.Ok) return tips = QtCore.QCoreApplication.translate('main', '准备导入数据库:') + folder_name + \ QtCore.QCoreApplication.translate('main', '\n此数据库中的数据将会被全部改写\n是否确认?') reply = QMessageBox.question( self, QtCore.QCoreApplication.translate('main', '导入确认'), tips, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply != QMessageBox.Yes: return mongodb_name = folder_name mongodb_bin = self.__line_mongo_db_binary.text() mongodb_host = self.__line_nosql_db_host.text() mongodb_port = self.__line_nosql_db_port.text() import_binary = path.join(mongodb_bin, 'mongorestore.exe') import_command = '"' + import_binary + '"' + \ ' --drop '\ ' --host ' + mongodb_host + \ ' --port ' + mongodb_port + \ ' -d ' + mongodb_name + \ ' ' + folder self.execute_command(import_command) def on_button_export(self): if not self.__check_alarm_mongodb_config(): return folder = str( QFileDialog.getExistingDirectory(self, "Select MongoDB Data Directory", directory=path.dirname( path.abspath(__file__)))) if folder == '': return mongodb_bin = self.__line_mongo_db_binary.text() mongodb_host = self.__line_nosql_db_host.text() mongodb_port = self.__line_nosql_db_port.text() export_binary = path.join(mongodb_bin, 'mongodump.exe') export_command_sd = '"' + export_binary + '"' + \ ' -h ' + mongodb_host + ':' + mongodb_port + \ ' -d ' + 'StockDaily' + \ ' -o ' + folder export_command_sas = '"' + export_binary + '"' + \ ' -h ' + mongodb_host + ':' + mongodb_port + \ ' -d ' + 'StockAnalysisSystem' + \ ' -o ' + folder # Workaround self.__pending_command.append(export_command_sas) self.execute_command(export_command_sd) # ----------------------------------------------------------------------------------- def __config_to_ui(self): sas = StockAnalysisSystem() config = sas.get_config() if config.load_config(): text = config.get('TS_TOKEN') self.__line_ts_token.setText(text) text = config.get('NOSQL_DB_HOST') self.__line_nosql_db_host.setText(text) self.__line_nosql_db_port.setText(config.get('NOSQL_DB_PORT')) self.__line_nosql_db_user.setText(config.get('NOSQL_DB_USER')) self.__line_nosql_db_pass.setText(config.get('NOSQL_DB_PASS')) self.__combo_web_proxy_protocol.setEditText( config.get('PROXY_PROTOCOL')) self.__combo_web_proxy_protocol.setCurrentIndex(0) self.__line_web_proxy_host.setText(config.get('PROXY_HOST')) def __ui_to_config(self): sas = StockAnalysisSystem() config = sas.get_config() config.set('TS_TOKEN', self.__line_ts_token.text()) config.set('NOSQL_DB_HOST', self.__line_nosql_db_host.text()) config.set('NOSQL_DB_PORT', self.__line_nosql_db_port.text()) config.set('NOSQL_DB_USER', self.__line_nosql_db_user.text()) config.set('NOSQL_DB_PASS', self.__line_nosql_db_pass.text()) config.set('PROXY_PROTOCOL', self.__combo_web_proxy_protocol.currentText()) config.set('PROXY_HOST', self.__line_web_proxy_host.text()) config.save_config() def __check_alarm_mongodb_config(self) -> bool: if self.__line_mongo_db_binary.text().strip() == '': QMessageBox.information( self, QtCore.QCoreApplication.translate('main', 'MongoDB配置错误'), QtCore.QCoreApplication.translate('main', '请先设置MongoDB的bin目录'), QMessageBox.Ok, QMessageBox.Ok) return False if self.__line_nosql_db_host.text().strip() == '': QMessageBox.information( self, QtCore.QCoreApplication.translate('main', 'MongoDB配置错误'), QtCore.QCoreApplication.translate('main', '请先设置MongoDB的服务器地址'), QMessageBox.Ok, QMessageBox.Ok) return False if self.__line_nosql_db_port.text().strip() == '': QMessageBox.information( self, QtCore.QCoreApplication.translate('main', 'MongoDB配置错误'), QtCore.QCoreApplication.translate('main', '请先设置MongoDB的服务器端口'), QMessageBox.Ok, QMessageBox.Ok) return False return True # -------------------------------------------------------------------------- def execute_command(self, cmd: str): self.__process = QProcess() self.__process.setProcessChannelMode(QProcess.MergedChannels) self.__process.started.connect(self.on_command_start) self.__process.finished.connect(self.on_command_finished) self.__process.readyReadStandardOutput.connect(self.read_output) self.__process.start(cmd) def on_command_start(self): tips = 'Process started...' self.__text_information.append(tips) print(tips) def on_command_finished(self): if len(self.__pending_command) > 0: cmd = self.__pending_command.pop(0) self.execute_command(cmd) return tips = 'Process finished...' print(tips) self.__text_information.append(tips) self.__text_information.append( '注意:sqlite数据库文件[ Data/sAsUtility.db ]需要手动进行备份或替换') def read_output(self): output = bytes( self.__process.readAllStandardOutput()).decode('UTF-8').strip() self.__text_information.append(output)
class GitBisectLogBrowserDialog(QWidget, Ui_GitBisectLogBrowserDialog): """ Class implementing a dialog to browse the bisect log history. """ CommitIdColumn = 0 OperationColumn = 1 SubjectColumn = 2 def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent reference to the parent widget (QWidget) """ super(GitBisectLogBrowserDialog, self).__init__(parent) self.setupUi(self) self.__position = QPoint() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.logTree.headerItem().setText(self.logTree.columnCount(), "") self.refreshButton = self.buttonBox.addButton( self.tr("&Refresh"), QDialogButtonBox.ActionRole) self.refreshButton.setToolTip( self.tr("Press to refresh the list of commits")) self.refreshButton.setEnabled(False) self.vcs = vcs self.repodir = "" self.__currentCommitId = "" self.__initData() self.__resetUI() self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) def __initData(self): """ Private method to (re-)initialize some data. """ self.buf = [] # buffer for stdout def closeEvent(self, e): """ Protected slot implementing a close event handler. @param e close event (QCloseEvent) """ if (self.process is not None and self.process.state() != QProcess.NotRunning): self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) self.__position = self.pos() e.accept() def show(self): """ Public slot to show the dialog. """ if not self.__position.isNull(): self.move(self.__position) self.__resetUI() super(GitBisectLogBrowserDialog, self).show() def __resetUI(self): """ Private method to reset the user interface. """ self.logTree.clear() def __resizeColumnsLog(self): """ Private method to resize the log tree columns. """ self.logTree.header().resizeSections(QHeaderView.ResizeToContents) self.logTree.header().setStretchLastSection(True) def __generateLogItem(self, commitId, operation, subject): """ Private method to generate a bisect log tree entry. @param commitId commit id info (string) @param operation bisect operation (string) @param subject subject of the bisect log entry (string) @return reference to the generated item (QTreeWidgetItem) """ columnLabels = [ commitId, operation, subject, ] itm = QTreeWidgetItem(self.logTree, columnLabels) return itm def __getLogEntries(self): """ Private method to retrieve bisect log entries from the repository. """ self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.inputGroup.setEnabled(True) self.inputGroup.show() self.refreshButton.setEnabled(False) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() self.buf = [] self.cancelled = False self.errors.clear() self.intercept = False args = self.vcs.initCommand("bisect") args.append("log") self.process.kill() self.process.setWorkingDirectory(self.repodir) self.inputGroup.setEnabled(True) self.inputGroup.show() self.process.start('git', args) procStarted = self.process.waitForStarted(5000) if not procStarted: self.inputGroup.setEnabled(False) self.inputGroup.hide() E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.').format('git')) def start(self, projectdir): """ Public slot to start the git bisect log command. @param projectdir directory name of the project (string) """ self.errorGroup.hide() QApplication.processEvents() self.__initData() # find the root of the repo self.repodir = projectdir while not os.path.isdir(os.path.join(self.repodir, self.vcs.adminDir)): self.repodir = os.path.dirname(self.repodir) if os.path.splitdrive(self.repodir)[1] == os.sep: return self.activateWindow() self.raise_() self.logTree.clear() self.__getLogEntries() def __procFinished(self, exitCode, exitStatus): """ Private slot connected to the finished signal. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) """ self.__processBuffer() self.__finish() def __finish(self): """ Private slot called when the process finished or the user pressed the button. """ if (self.process is not None and self.process.state() != QProcess.NotRunning): self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) QApplication.restoreOverrideCursor() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.inputGroup.setEnabled(False) self.inputGroup.hide() self.refreshButton.setEnabled(True) def __processBuffer(self): """ Private method to process the buffered output of the git log command. """ for line in self.buf: line = line.rstrip() if line.startswith("# "): operation, commitId, subject = line[2:].split(None, 2) # commit is surrounded by [...], abbreviate to 20 chars commitId = commitId[1:-1][:20] # operation is followed by ':' operation = operation[:-1] self.__generateLogItem(commitId, operation, subject) self.__resizeColumnsLog() self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) # restore current item if self.__currentCommitId: items = self.logTree.findItems(self.__currentCommitId, Qt.MatchExactly, self.CommitIdColumn) if items: self.logTree.setCurrentItem(items[0]) self.__currentCommitId = "" def __readStdout(self): """ Private slot to handle the readyReadStandardOutput signal. It reads the output of the process and inserts it into a buffer. """ self.process.setReadChannel(QProcess.StandardOutput) while self.process.canReadLine(): line = str(self.process.readLine(), Preferences.getSystem("IOEncoding"), 'replace') self.buf.append(line) def __readStderr(self): """ Private slot to handle the readyReadStandardError signal. It reads the error output of the process and inserts it into the error pane. """ if self.process is not None: s = str(self.process.readAllStandardError(), Preferences.getSystem("IOEncoding"), 'replace') self.__showError(s) def __showError(self, out): """ Private slot to show some error. @param out error to be shown (string) """ self.errorGroup.show() self.errors.insertPlainText(out) self.errors.ensureCursorVisible() def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @param button button that was clicked (QAbstractButton) """ if button == self.buttonBox.button(QDialogButtonBox.Close): self.close() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): self.cancelled = True self.__finish() elif button == self.refreshButton: self.on_refreshButton_clicked() @pyqtSlot() def on_refreshButton_clicked(self): """ Private slot to refresh the log. """ # save the current item's commit ID itm = self.logTree.currentItem() if itm is not None: self.__currentCommitId = itm.text(self.CommitIdColumn) else: self.__currentCommitId = "" self.start(self.repodir) def on_passwordCheckBox_toggled(self, isOn): """ Private slot to handle the password checkbox toggled. @param isOn flag indicating the status of the check box (boolean) """ if isOn: self.input.setEchoMode(QLineEdit.Password) else: self.input.setEchoMode(QLineEdit.Normal) @pyqtSlot() def on_sendButton_clicked(self): """ Private slot to send the input to the git process. """ inputTxt = self.input.text() inputTxt += os.linesep if self.passwordCheckBox.isChecked(): self.errors.insertPlainText(os.linesep) self.errors.ensureCursorVisible() else: self.errors.insertPlainText(inputTxt) self.errors.ensureCursorVisible() self.errorGroup.show() self.process.write(strToQByteArray(inputTxt)) self.passwordCheckBox.setChecked(False) self.input.clear() def on_input_returnPressed(self): """ Private slot to handle the press of the return key in the input field. """ self.intercept = True self.on_sendButton_clicked() def keyPressEvent(self, evt): """ Protected slot to handle a key press event. @param evt the key press event (QKeyEvent) """ if self.intercept: self.intercept = False evt.accept() return super(GitBisectLogBrowserDialog, self).keyPressEvent(evt)
def start_process( self, script_name, working_directory, interactive=True, debugger=False, command_args=None, envars=None, runner=None, python_args=None, ): """ Start the child Python process. Will run the referenced Python script_name within the context of the working directory. If interactive is True (the default) the Python process will run in interactive mode (dropping the user into the REPL when the script completes). If debugger is True (the default is False) then the script will run within a debug runner session. If there is a list of command_args (the default is None), then these will be passed as further arguments into the script to be run. If there is a list of environment variables, these will be part of the context of the new child process. If runner is given, this is used as the command to start the Python process. If python_args is given, these are passed as arguments to the Python runtime used to launch the child process. """ if not envars: # Envars must be a list if not passed a value. envars = [] self.script = "" if script_name: self.script = os.path.abspath(os.path.normcase(script_name)) logger.info("Running script: {}".format(self.script)) if interactive: logger.info("Running with interactive mode.") if command_args is None: command_args = [] logger.info("Command args: {}".format(command_args)) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) # Force buffers to flush immediately. env = QProcessEnvironment.systemEnvironment() env.insert("PYTHONUNBUFFERED", "1") env.insert("PYTHONIOENCODING", "utf-8") if sys.platform == "darwin": # Ensure the correct encoding is set for the environment. If the # following two lines are not set, then Flask will complain about # Python 3 being misconfigured to use ASCII encoding. # See: https://click.palletsprojects.com/en/7.x/python3/ encoding = "{}.utf-8".format(language_code) env.insert("LC_ALL", encoding) env.insert("LANG", encoding) if sys.platform == "win32" and "pythonw.exe" in sys.executable: # On Windows, if installed via NSIS then Python is always run in # isolated mode via pythonw.exe so none of the expected directories # are on sys.path. To mitigate, Mu attempts to drop a mu.pth file # in a location taken from Windows based settings. This file will # contain the "other" directories to include on the Python path, # such as the working_directory and, if different from the # working_directory, the directory containing the script to run. try: if site.ENABLE_USER_SITE: # Ensure the USER_SITE directory exists. os.makedirs(site.getusersitepackages(), exist_ok=True) site_path = site.USER_SITE path_file = os.path.join(site_path, "mu.pth") logger.info("Python paths set via {}".format(path_file)) # Copy current Python paths. Use a set to avoid # duplications. paths_to_use = set([os.path.normcase(p) for p in sys.path]) # Add Mu's working directory. paths_to_use.add(os.path.normcase(working_directory)) # Add the directory containing the script. paths_to_use.add( os.path.normcase(os.path.dirname(self.script))) # Dropping a mu.pth file containing the paths_to_use # into USER_SITE will add such paths to sys.path in the # child process. with open(path_file, "w") as mu_pth: for p in paths_to_use: mu_pth.write(p + "\n") else: logger.info("Unable to set Python paths." " Python's USER_SITE not enabled." " Check configuration with administrator.") except Exception as ex: # Log all possible errors and allow Mu to continue. This is a # "best effort" attempt to add the correct paths to the child # process, but sometimes configuration by sys-admins may cause # this to fail. logger.error("Could not set Python paths with mu.pth file.") logger.error(ex) if "PYTHONPATH" not in envars: envars.append(("PYTHONPATH", os.pathsep.join(sys.path))) if envars: logger.info("Running with environment variables: " "{}".format(envars)) for name, value in envars: env.insert(name, value) logger.info("Working directory: {}".format(working_directory)) self.process.setWorkingDirectory(working_directory) self.process.setProcessEnvironment(env) self.process.readyRead.connect(self.try_read_from_stdout) self.process.finished.connect(self.finished) logger.info("Python path: {}".format(sys.path)) if debugger: # Start the mu-debug runner for the script. parent_dir = os.path.join(os.path.dirname(__file__), "..") mu_dir = os.path.abspath(parent_dir) runner = os.path.join(mu_dir, "mu-debug.py") python_exec = sys.executable args = [runner, self.script] + command_args self.process.start(python_exec, args) else: if runner: # Use the passed in Python "runner" to run the script. python_exec = runner else: # Use the current system Python to run the script. python_exec = sys.executable args = [] if self.script: if interactive: # Start the script in interactive Python mode. args = ["-i", self.script] + command_args else: # Just run the command with no additional flags. args = [self.script] + command_args if python_args: args = python_args + args logger.info("Runner: {}".format(python_exec)) logger.info("Args: {}".format(args)) self.process.start(python_exec, args) self.running = True
class PythonProcessPane(QTextEdit): """ Handles / displays a Python process's stdin/out with working command history and simple buffer editing. """ on_append_text = pyqtSignal(bytes) def __init__(self, parent=None): super().__init__(parent) self.setFont(Font().load()) self.setAcceptRichText(False) self.setReadOnly(False) self.setUndoRedoEnabled(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.context_menu) self.running = False # Flag to show the child process is running. self.setObjectName("PythonRunner") self.process = None # Will eventually reference the running process. self.input_history = [] # history of inputs entered in this session. self.start_of_current_line = 0 # start position of the input line. self.history_position = 0 # current position when navigation history. self.stdout_buffer = b"" # contains non-decoded bytes from stdout. self.reading_stdout = False # flag showing if already reading stdout. def start_process( self, script_name, working_directory, interactive=True, debugger=False, command_args=None, envars=None, runner=None, python_args=None, ): """ Start the child Python process. Will run the referenced Python script_name within the context of the working directory. If interactive is True (the default) the Python process will run in interactive mode (dropping the user into the REPL when the script completes). If debugger is True (the default is False) then the script will run within a debug runner session. If there is a list of command_args (the default is None), then these will be passed as further arguments into the script to be run. If there is a list of environment variables, these will be part of the context of the new child process. If runner is given, this is used as the command to start the Python process. If python_args is given, these are passed as arguments to the Python runtime used to launch the child process. """ if not envars: # Envars must be a list if not passed a value. envars = [] self.script = "" if script_name: self.script = os.path.abspath(os.path.normcase(script_name)) logger.info("Running script: {}".format(self.script)) if interactive: logger.info("Running with interactive mode.") if command_args is None: command_args = [] logger.info("Command args: {}".format(command_args)) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) # Force buffers to flush immediately. env = QProcessEnvironment.systemEnvironment() env.insert("PYTHONUNBUFFERED", "1") env.insert("PYTHONIOENCODING", "utf-8") if sys.platform == "darwin": # Ensure the correct encoding is set for the environment. If the # following two lines are not set, then Flask will complain about # Python 3 being misconfigured to use ASCII encoding. # See: https://click.palletsprojects.com/en/7.x/python3/ encoding = "{}.utf-8".format(language_code) env.insert("LC_ALL", encoding) env.insert("LANG", encoding) if sys.platform == "win32" and "pythonw.exe" in sys.executable: # On Windows, if installed via NSIS then Python is always run in # isolated mode via pythonw.exe so none of the expected directories # are on sys.path. To mitigate, Mu attempts to drop a mu.pth file # in a location taken from Windows based settings. This file will # contain the "other" directories to include on the Python path, # such as the working_directory and, if different from the # working_directory, the directory containing the script to run. try: if site.ENABLE_USER_SITE: # Ensure the USER_SITE directory exists. os.makedirs(site.getusersitepackages(), exist_ok=True) site_path = site.USER_SITE path_file = os.path.join(site_path, "mu.pth") logger.info("Python paths set via {}".format(path_file)) # Copy current Python paths. Use a set to avoid # duplications. paths_to_use = set([os.path.normcase(p) for p in sys.path]) # Add Mu's working directory. paths_to_use.add(os.path.normcase(working_directory)) # Add the directory containing the script. paths_to_use.add( os.path.normcase(os.path.dirname(self.script))) # Dropping a mu.pth file containing the paths_to_use # into USER_SITE will add such paths to sys.path in the # child process. with open(path_file, "w") as mu_pth: for p in paths_to_use: mu_pth.write(p + "\n") else: logger.info("Unable to set Python paths." " Python's USER_SITE not enabled." " Check configuration with administrator.") except Exception as ex: # Log all possible errors and allow Mu to continue. This is a # "best effort" attempt to add the correct paths to the child # process, but sometimes configuration by sys-admins may cause # this to fail. logger.error("Could not set Python paths with mu.pth file.") logger.error(ex) if "PYTHONPATH" not in envars: envars.append(("PYTHONPATH", os.pathsep.join(sys.path))) if envars: logger.info("Running with environment variables: " "{}".format(envars)) for name, value in envars: env.insert(name, value) logger.info("Working directory: {}".format(working_directory)) self.process.setWorkingDirectory(working_directory) self.process.setProcessEnvironment(env) self.process.readyRead.connect(self.try_read_from_stdout) self.process.finished.connect(self.finished) logger.info("Python path: {}".format(sys.path)) if debugger: # Start the mu-debug runner for the script. parent_dir = os.path.join(os.path.dirname(__file__), "..") mu_dir = os.path.abspath(parent_dir) runner = os.path.join(mu_dir, "mu-debug.py") python_exec = sys.executable args = [runner, self.script] + command_args self.process.start(python_exec, args) else: if runner: # Use the passed in Python "runner" to run the script. python_exec = runner else: # Use the current system Python to run the script. python_exec = sys.executable args = [] if self.script: if interactive: # Start the script in interactive Python mode. args = ["-i", self.script] + command_args else: # Just run the command with no additional flags. args = [self.script] + command_args if python_args: args = python_args + args logger.info("Runner: {}".format(python_exec)) logger.info("Args: {}".format(args)) self.process.start(python_exec, args) self.running = True def finished(self, code, status): """ Handle when the child process finishes. """ self.running = False cursor = self.textCursor() cursor.movePosition(cursor.End) cursor.insertText("\n\n---------- FINISHED ----------\n") msg = "exit code: {} status: {}".format(code, status) cursor.insertText(msg) cursor.movePosition(QTextCursor.End) self.setTextCursor(cursor) self.setReadOnly(True) def context_menu(self): """ Creates custom context menu with just copy and paste. """ menu = QMenu(self) if platform.system() == "Darwin": copy_keys = QKeySequence(Qt.CTRL + Qt.Key_C) paste_keys = QKeySequence(Qt.CTRL + Qt.Key_V) else: copy_keys = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_C) paste_keys = QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_V) menu.addAction("Copy", self.copy, copy_keys) menu.addAction("Paste", self.paste, paste_keys) menu.exec_(QCursor.pos()) def paste(self): """ Grabs clipboard contents then writes to the REPL. """ clipboard = QApplication.clipboard() if clipboard and clipboard.text(): # normalize for Windows line-ends. text = "\n".join(clipboard.text().splitlines()) if text: self.parse_paste(text) def parse_paste(self, text): """ Recursively takes characters from text to be parsed as input. We do this so the event loop has time to respond to output from the process to which the characters are sent (for example, when a newline is sent). Yes, this is a quick and dirty hack, but ensures the pasted input is also evaluated in an interactive manner rather than as a single-shot splurge of data. Essentially, it's simulating someone typing in the characters of the pasted text *really fast* but in such a way that the event loop cycles. """ character = text[0] # the current character to process. remainder = text[1:] # remaining characters to process in the future. if character.isprintable() or character in string.printable: if character == "\n" or character == "\r": self.parse_input(Qt.Key_Enter, character, None) else: self.parse_input(None, character, None) if remainder: # Schedule a recursive call of parse_paste with the remaining text # to process. This allows the event loop to cycle and handle any # output from the child process as a result of the text pasted so # far (especially useful for handling responses from newlines). QTimer.singleShot(2, lambda text=remainder: self.parse_paste(text)) def keyPressEvent(self, data): """ Called when the user types something in the REPL. """ key = data.key() text = data.text() modifiers = data.modifiers() self.parse_input(key, text, modifiers) def on_process_halt(self): """ Called when the the user has manually halted a running process. Ensures that the remaining data from the halted process's stdout is handled properly. When the process is halted the user is dropped into the Python prompt and this method ensures the UI is updated in a clean, non-blocking way. """ data = self.process.readAll().data() if data: while True: try: self.append(data) self.on_append_text.emit(data) self.set_start_of_current_line() break except UnicodeDecodeError: # Discard problematic start byte and try again. # (This may be caused by a split in multi-byte characters). data = data[1:] def parse_input(self, key, text, modifiers): """ Correctly encodes user input and sends it to the connected process. The key is a Qt.Key_Something value, text is the textual representation of the input, and modifiers are the control keys (shift, CTRL, META, etc) also used. """ msg = b"" # Eventually to be inserted into the pane at the cursor. if key == Qt.Key_Enter or key == Qt.Key_Return: msg = b"\n" elif (platform.system() == "Darwin" and modifiers == Qt.MetaModifier) or (platform.system() != "Darwin" and modifiers == Qt.ControlModifier): # Handle CTRL-C and CTRL-D if self.process and self.running: pid = self.process.processId() # NOTE: Windows related constraints don't allow us to send a # CTRL-C, rather, the process will just terminate. halt_flag = False if key == Qt.Key_C: halt_flag = True os.kill(pid, signal.SIGINT) if key == Qt.Key_D: halt_flag = True self.process.kill() if halt_flag: # Clean up from kill signal. self.process.readAll() # Discard queued output. self.stdout_buffer = b"" # Schedule update of the UI after the process halts (in # next iteration of the event loop). QTimer.singleShot(1, self.on_process_halt) return elif key == Qt.Key_Up: self.history_back() elif key == Qt.Key_Down: self.history_forward() elif key == Qt.Key_Right: cursor = self.textCursor() cursor.movePosition(QTextCursor.Right) self.setTextCursor(cursor) elif key == Qt.Key_Left: cursor = self.textCursor() if cursor.position() > self.start_of_current_line: cursor.movePosition(QTextCursor.Left) self.setTextCursor(cursor) elif key == Qt.Key_Home: cursor = self.textCursor() cursor.movePosition(QTextCursor.End) buffer_len = len(self.toPlainText()) - self.start_of_current_line for i in range(buffer_len): cursor.movePosition(QTextCursor.Left) self.setTextCursor(cursor) elif key == Qt.Key_End: cursor = self.textCursor() cursor.movePosition(QTextCursor.End) self.setTextCursor(cursor) elif (modifiers == Qt.ControlModifier | Qt.ShiftModifier) or (platform.system() == "Darwin" and modifiers == Qt.ControlModifier): # Command-key on Mac, Ctrl-Shift on Win/Lin if key == Qt.Key_C: self.copy() elif key == Qt.Key_V: self.paste() elif text.isprintable(): # If the key is for a printable character then add it to the # active buffer and display it. msg = bytes(text, "utf8") if key == Qt.Key_Backspace: self.backspace() if key == Qt.Key_Delete: self.delete() if key == Qt.Key_Enter or key == Qt.Key_Return: # First move cursor to the end of the line and insert newline in # case return/enter is pressed while the cursor is in the # middle of the line cursor = self.textCursor() cursor.movePosition(QTextCursor.End) self.setTextCursor(cursor) self.insert(msg) # Then write line to std_in and add to history content = self.toPlainText() line = content[self.start_of_current_line:].encode("utf-8") self.write_to_stdin(line) if line.strip(): self.input_history.append(line.replace(b"\n", b"")) self.history_position = 0 self.set_start_of_current_line() elif not self.isReadOnly() and msg: self.insert(msg) def set_start_of_current_line(self): """ Set the flag to indicate the start of the current line (used before waiting for input). This flag is used to discard the preceeding text in the text entry field when Mu parses new input from the user (i.e. any text beyond the self.start_of_current_line). """ self.start_of_current_line = len(self.toPlainText()) def history_back(self): """ Replace the current input line with the next item BACK from the current history position. """ if self.input_history: self.history_position -= 1 history_pos = len(self.input_history) + self.history_position if history_pos < 0: self.history_position += 1 history_pos = 0 history_item = self.input_history[history_pos] self.replace_input_line(history_item) def history_forward(self): """ Replace the current input line with the next item FORWARD from the current history position. """ if self.input_history: self.history_position += 1 history_pos = len(self.input_history) + self.history_position if history_pos >= len(self.input_history): # At the most recent command. self.history_position = 0 self.clear_input_line() return history_item = self.input_history[history_pos] self.replace_input_line(history_item) def try_read_from_stdout(self): """ Ensure reading from stdout only happens if there is NOT already current attempts to read from stdout. """ if not self.reading_stdout: self.reading_stdout = True self.read_from_stdout() def read_from_stdout(self): """ Process incoming data from the process's stdout. """ data = self.process.read(256) if data: data = self.stdout_buffer + data try: self.append(data) self.on_append_text.emit(data) self.set_start_of_current_line() self.stdout_buffer = b"" except UnicodeDecodeError: self.stdout_buffer = data QTimer.singleShot(2, self.read_from_stdout) else: self.reading_stdout = False def write_to_stdin(self, data): """ Writes data from the Qt application to the child process's stdin. """ if self.process: self.process.write(data) def append(self, msg): """ Append text to the text area. """ cursor = self.textCursor() cursor.movePosition(QTextCursor.End) cursor.insertText(msg.decode("utf-8")) cursor.movePosition(QTextCursor.End) self.setTextCursor(cursor) def insert(self, msg): """ Insert text to the text area at the current cursor position. """ cursor = self.textCursor() if cursor.position() < self.start_of_current_line: cursor.movePosition(QTextCursor.End) cursor.insertText(msg.decode("utf-8")) self.setTextCursor(cursor) def backspace(self): """ Removes a character from the current buffer -- to the left of cursor. """ cursor = self.textCursor() if cursor.position() > self.start_of_current_line: cursor = self.textCursor() cursor.deletePreviousChar() self.setTextCursor(cursor) def delete(self): """ Removes a character from the current buffer -- to the right of cursor. """ cursor = self.textCursor() if cursor.position() >= self.start_of_current_line: cursor.deleteChar() self.setTextCursor(cursor) def clear_input_line(self): """ Remove all the characters currently in the input buffer line. """ cursor = self.textCursor() cursor.movePosition(QTextCursor.End) buffer_len = len(self.toPlainText()) - self.start_of_current_line for i in range(buffer_len): cursor.deletePreviousChar() self.setTextCursor(cursor) def replace_input_line(self, text): """ Replace the current input line with the passed in text. """ self.clear_input_line() self.append(text) def set_font_size(self, new_size=DEFAULT_FONT_SIZE): """ Sets the font size for all the textual elements in this pane. """ f = self.font() f.setPointSize(new_size) self.setFont(f) def set_zoom(self, size): """ Set the current zoom level given the "t-shirt" size. """ self.set_font_size(PANE_ZOOM_SIZES[size]) def set_theme(self, theme): pass
def test_process(self): proc = QProcess() proc.start('python3', ["/home/kali/Desktop/Sublist3r/sublist3r.py", '-d', 'v.zzu.edu.cn']) proc.execute('python3 /home/kali/Desktop/Sublist3r/sublist3r.py -d v.zzu.edu.cn') proc.start('python3', ['/home/kali/Desktop/Sublist3r/sublist3r.py', '-d', 'www.v.zzu.edu.cn']) proc.execute('whatweb baidu.com') proc.start('touch test.txt') proc.execute('nmap' ,['baidu.com']) proc.waitForFinished() result = str(proc.readAllStandardOutput()) proc.execute('dnsenum ')
class GServer(): def __init__(self): self.HOST = 'localhost' self.PORT = 5555 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.serverWidget = None self.game = None self.title = "GXEPHYRSV" self.process = QProcess() self.sender = senderObject() from random import randint self.display = ":" + str(randint(101, 999)) def kill(self): self.__del__() def __del__(self): print("Server dead") self.process.kill() # Inicia o display virtual Xephyr em um widget def getServerWidget(self): if self.serverWidget is not None: return self.serverWidget print("Iniciando Xephyr!") system("\"C:\\Program Files (x86)\\Xming\\Xming.exe\" :0 -clipboard -multiwindow") sleep(1) self.process.start("ubuntu1804 -c DISPLAY=:0 Xephyr -ac -br -resizeable -no-host-grab -reset -terminate -screen 640x480 " + self.display + " -title " + self.title) tries = 0 stdout = b'' # Espera inicialização do Xephyr e # extrai o winId da janela do display virtual winId = -1 while winId == -1: sleep(1) if tries == 10: raise Exception("Servidor Xephyr não encontrado!") tries += 1 def callbackDEF(h, hh): if win32gui.IsWindowVisible (h) and win32gui.IsWindowEnabled (h): hh.append(h) return True hwnds = [] win32gui.EnumWindows(callbackDEF, hwnds) for win in hwnds: if win32gui.GetWindowText(win) == self.title: winId = int(win) break self.new_window = QtGui.QWindow.fromWinId(winId) print("winId = " + str(winId)) # FramelessWindow permite "colar" a janela do servidor na janela do editor self.new_window.setFlags(Qt.FramelessWindowHint) self.game_widget = QtWidgets.QWidget.createWindowContainer(self.new_window) # O widget que recebe o vídeo deve ser algum widget que tenha # informações visuais, como o textEdit ou o graphicsView self.serverWidget = QtWidgets.QWidget() # De fato "cola" a janela do servidor dentro de um widget self.game_box = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight, self.serverWidget) self.game_box.addWidget(self.game_widget) return self.serverWidget # Avatar precisa de uma thread def startGameThread(self): print("Iniciando o avatar") location = "/mnt/" + GDefaultValues.cwd[0].lower() + GDefaultValues.cwd[2:] system("ubuntu1804 -c DISPLAY=" + self.display + " " + location + "/unityVideo/videoCreator.x86_64 teste_renderer 0 30 32 37 -screen-fullscreen 0 -screen-quality Fantastic -force-opengl") def startCommunication(self): if self.game is None: self.game = threading.Thread(target=self.startGameThread) self.game.start() sleep(2) try: print(self.game.is_alive()) print("Connectando em %s %d" % (self.HOST, self.PORT)) self.sock.connect((self.HOST, self.PORT)) return 0 except: print("Servidor não encontrado") return 1 def send(self, text): text = GParser().cleanText(text) blocks = GParser().getCommandBlocks(text) try: i = 0 for msg in blocks: if msg[0] in (' ', '\n', '\t'): msg = msg[1:] i += 1 print("ENVIANDO: %s" % (msg)) self.sock.send(msg.encode('utf-8')) self.sock.recv(2048) except: print("Não há conexação com o servidor") def sendToRecord(self, text, vName): threading.Thread(target=self.waitingSend, args=(text, vName)).start() def waitingSend(self, text, vName): text = GParser().cleanText(text) blocks = GParser().getCommandBlocks(text) try: for msg in blocks: while msg[0] in (' ', '\n', '\t'): msg = msg[1:] if msg == "": continue msg = msg.replace('\n\r', '\n').replace(chr(0x2028), '\n').replace(chr(0x2029), '\n') txts = msg.split('\n') for txt in txts: if txt == "" or txt.isspace(): continue txt = txt.encode('utf-8') self.sock.send(txt) self.sock.recv(2048) # Ao final da gravação o avatar envia mais uma mensagem self.sock.recv(2048) self.sender.finishedRecording.emit(vName) except: print("Não há conexação com o servidor")
class QuantyDockWidget(QDockWidget): _defaults = { 'element': 'Ni', 'charge': '2+', 'symmetry': 'Oh', 'experiment': 'XAS', 'edge': 'L2,3 (2p)', 'temperature': 10.0, 'magneticField': (0.0, 0.0, 0.0), 'shells': None, 'nPsis': None, 'energies': None, 'hamiltonianParameters': None, 'hamiltonianTermsCheckState': None, 'spectra': None, 'templateName': None, 'baseName': 'untitled', 'label': None, 'uuid': None, 'startingTime': None, 'endingTime': None, } def __init__(self): super(QuantyDockWidget, self).__init__() self.__dict__.update(self._defaults) # Load the external .ui file for the widget. path = resourceFileName(os.path.join('gui', 'uis', 'quanty.ui')) loadUi(path, baseinstance=self, package='crispy.gui') # Remove macOS focus border. for child in self.findChildren((QListView, TreeView, QDoubleSpinBox)): child.setAttribute(Qt.WA_MacShowFocusRect, False) self.setParameters() self.activateUi() def setParameters(self): # Load the external parameters. path = resourceFileName( os.path.join('modules', 'quanty', 'parameters', 'ui.json')) with open(path) as p: uiParameters = json.loads( p.read(), object_pairs_hook=collections.OrderedDict) self.elements = uiParameters['elements'] if self.element not in self.elements: self.element = tuple(self.elements)[0] self.charges = self.elements[self.element]['charges'] if self.charge not in self.charges: self.charge = tuple(self.charges)[0] self.symmetries = self.charges[self.charge]['symmetries'] if self.symmetry not in self.symmetries: self.symmetry = tuple(self.symmetries)[0] self.experiments = self.symmetries[self.symmetry]['experiments'] if self.experiment not in self.experiments: self.experiment = tuple(self.experiments)[0] self.edges = self.experiments[self.experiment]['edges'] if self.edge not in self.edges: self.edge = tuple(self.edges)[0] branch = self.edges[self.edge] self.shells = branch['shells'] self.nPsis = branch['nPsis'] self.energies = branch['energies'] self.templateName = branch['template name'] self.hamiltonians = branch['configurations'] path = resourceFileName( os.path.join('modules', 'quanty', 'parameters', 'hamiltonian.json')) with open(path) as p: hamiltonianParameters = json.loads( p.read(), object_pairs_hook=collections.OrderedDict) self.hamiltonianParameters = OrderedDict() self.hamiltonianTermsCheckState = collections.OrderedDict() for hamiltonian in self.hamiltonians: label = '{} Hamiltonian'.format(hamiltonian[0]) configuration = hamiltonian[1] terms = (hamiltonianParameters['elements'][ self.element]['charges'][self.charge]['configurations'] [configuration]['Hamiltonian Terms']) for term in terms: if ('Coulomb' in term) or ('Spin-Orbit Coupling' in term): parameters = terms[term] else: try: parameters = terms[term][self.symmetry] except KeyError: continue for parameter in parameters: if parameter[0] in ('F', 'G'): scaling = 0.8 else: scaling = 1.0 self.hamiltonianParameters[term][label][parameter] = ( parameters[parameter], scaling) if 'Hybridization' in term: self.hamiltonianTermsCheckState[term] = 0 else: self.hamiltonianTermsCheckState[term] = 2 self.setUi() def updateParameters(self): self.element = self.elementComboBox.currentText() self.charge = self.chargeComboBox.currentText() self.symmetry = self.symmetryComboBox.currentText() self.experiment = self.experimentComboBox.currentText() self.edge = self.edgeComboBox.currentText() self.baseName = self._defaults['baseName'] self.updateMainWindowTitle() self.setParameters() def setUi(self): # Set the values for the combo boxes. self.elementComboBox.setItems(self.elements, self.element) self.chargeComboBox.setItems(self.charges, self.charge) self.symmetryComboBox.setItems(self.symmetries, self.symmetry) self.experimentComboBox.setItems(self.experiments, self.experiment) self.edgeComboBox.setItems(self.edges, self.edge) # Set the temperature spin box. self.temperatureDoubleSpinBox.setValue(self.temperature) # Set the magnetic field spin boxes. self.magneticFieldXDoubleSpinBox.setValue(self.magneticField[0]) self.magneticFieldYDoubleSpinBox.setValue(self.magneticField[1]) self.magneticFieldZDoubleSpinBox.setValue(self.magneticField[2]) # Set the labels, ranges, etc. self.energiesTabWidget.setTabText(0, self.energies[0][0]) self.e1MinDoubleSpinBox.setValue(self.energies[0][1]) self.e1MaxDoubleSpinBox.setValue(self.energies[0][2]) self.e1NPointsDoubleSpinBox.setValue(self.energies[0][3]) self.e1LorentzianBroadeningDoubleSpinBox.setValue(self.energies[0][4]) if 'RIXS' in self.experiment: tab = self.energiesTabWidget.findChild(QWidget, 'e2Tab') self.energiesTabWidget.addTab(tab, tab.objectName()) self.energiesTabWidget.setTabText(1, self.energies[1][0]) self.e2MinDoubleSpinBox.setValue(self.energies[1][1]) self.e2MaxDoubleSpinBox.setValue(self.energies[1][2]) self.e2NPointsDoubleSpinBox.setValue(self.energies[1][3]) self.e2LorentzianBroadeningDoubleSpinBox.setValue( self.energies[1][4]) self.e1GaussianBroadeningDoubleSpinBox.setEnabled(False) self.e2GaussianBroadeningDoubleSpinBox.setEnabled(False) else: self.energiesTabWidget.removeTab(1) self.e1GaussianBroadeningDoubleSpinBox.setEnabled(True) self.e2GaussianBroadeningDoubleSpinBox.setEnabled(True) self.nPsisDoubleSpinBox.setValue(self.nPsis) # Create the Hamiltonian model. self.hamiltonianModel = TreeModel(('Parameter', 'Value', 'Scaling'), self.hamiltonianParameters) self.hamiltonianModel.setNodesCheckState( self.hamiltonianTermsCheckState) # Assign the Hamiltonian model to the Hamiltonian terms view. self.hamiltonianTermsView.setModel(self.hamiltonianModel) self.hamiltonianTermsView.selectionModel().setCurrentIndex( self.hamiltonianModel.index(0, 0), QItemSelectionModel.Select) # Assign the Hamiltonian model to the Hamiltonian parameters view, and # set some properties. self.hamiltonianParametersView.setModel(self.hamiltonianModel) self.hamiltonianParametersView.expandAll() self.hamiltonianParametersView.resizeAllColumnsToContents() self.hamiltonianParametersView.setColumnWidth(0, 160) self.hamiltonianParametersView.setAlternatingRowColors(True) index = self.hamiltonianTermsView.currentIndex() self.hamiltonianParametersView.setRootIndex(index) self.hamiltonianTermsView.selectionModel().selectionChanged.connect( self.selectedHamiltonianTermChanged) # Set the sizes of the two views. self.hamiltonianSplitter.setSizes((120, 300)) # Create the results model and assign it to the view. if not hasattr(self, 'resultsModel'): self.resultsModel = ListModel() self.resultsView.setSelectionMode( QAbstractItemView.ExtendedSelection) self.resultsView.setModel(self.resultsModel) self.resultsView.selectionModel().selectionChanged.connect( self.selectedCalculationsChanged) # Add a context menu self.resultsView.setContextMenuPolicy(Qt.CustomContextMenu) self.resultsView.customContextMenuRequested[QPoint].connect( self.createContextMenu) self.resultsView.setAlternatingRowColors(True) def activateUi(self): self.elementComboBox.currentTextChanged.connect(self.updateParameters) self.chargeComboBox.currentTextChanged.connect(self.updateParameters) self.symmetryComboBox.currentTextChanged.connect(self.updateParameters) self.experimentComboBox.currentTextChanged.connect( self.updateParameters) self.edgeComboBox.currentTextChanged.connect(self.updateParameters) self.saveInputAsPushButton.clicked.connect(self.saveInputAs) self.calculationPushButton.clicked.connect(self.runCalculation) self.e1GaussianBroadeningDoubleSpinBox.valueChanged.connect( self.replot) self.e1LorentzianBroadeningDoubleSpinBox.valueChanged.connect( self.replot) def getParameters(self): self.element = self.elementComboBox.currentText() self.charge = self.chargeComboBox.currentText() self.symmetry = self.symmetryComboBox.currentText() self.experiment = self.experimentComboBox.currentText() self.edge = self.edgeComboBox.currentText() self.temperature = self.temperatureDoubleSpinBox.value() self.magneticField = ( self.magneticFieldXDoubleSpinBox.value(), self.magneticFieldYDoubleSpinBox.value(), self.magneticFieldZDoubleSpinBox.value(), ) self.nPsis = int(self.nPsisDoubleSpinBox.value()) if 'RIXS' in self.experiment: self.energies = ((self.energiesTabWidget.tabText(0), self.e1MinDoubleSpinBox.value(), self.e1MaxDoubleSpinBox.value(), int(self.e1NPointsDoubleSpinBox.value()), self.e1LorentzianBroadeningDoubleSpinBox.value(), self.e1GaussianBroadeningDoubleSpinBox.value()), (self.energiesTabWidget.tabText(1), self.e2MinDoubleSpinBox.value(), self.e2MaxDoubleSpinBox.value(), int(self.e2NPointsDoubleSpinBox.value()), self.e2LorentzianBroadeningDoubleSpinBox.value(), self.e2GaussianBroadeningDoubleSpinBox.value())) else: self.energies = ( (self.energiesTabWidget.tabText(0), self.e1MinDoubleSpinBox.value(), self.e1MaxDoubleSpinBox.value(), int(self.e1NPointsDoubleSpinBox.value()), self.e1LorentzianBroadeningDoubleSpinBox.value(), self.e1GaussianBroadeningDoubleSpinBox.value()), ) self.hamiltonianParameters = self.hamiltonianModel.getModelData() self.hamiltonianTermsCheckState = ( self.hamiltonianModel.getNodesCheckState()) def saveParameters(self): for key in self._defaults: try: self.calculation[key] = copy.deepcopy(self.__dict__[key]) except KeyError: self.calculation[key] = None def loadParameters(self, dictionary): for key in self._defaults: try: self.__dict__[key] = copy.deepcopy(dictionary[key]) except KeyError: self.__dict__[key] = None def createContextMenu(self, position): icon = QIcon(resourceFileName(os.path.join('gui', 'icons', 'save.svg'))) self.saveSelectedCalculationsAsAction = QAction( icon, 'Save Selected Calculations As...', self, triggered=self.saveSelectedCalculationsAs) icon = QIcon( resourceFileName(os.path.join('gui', 'icons', 'trash.svg'))) self.removeCalculationsAction = QAction( icon, 'Remove Selected Calculations', self, triggered=self.removeSelectedCalculations) self.removeAllCalculationsAction = QAction( icon, 'Remove All Calculations', self, triggered=self.removeAllCalculations) icon = QIcon( resourceFileName(os.path.join('gui', 'icons', 'folder-open.svg'))) self.loadCalculationsAction = QAction(icon, 'Load Calculations', self, triggered=self.loadCalculations) selection = self.resultsView.selectionModel().selection() selectedItemsRegion = self.resultsView.visualRegionForSelection( selection) cursorPosition = self.resultsView.mapFromGlobal(QCursor.pos()) if selectedItemsRegion.contains(cursorPosition): contextMenu = QMenu('Items Context Menu', self) contextMenu.addAction(self.saveSelectedCalculationsAsAction) contextMenu.addAction(self.removeCalculationsAction) contextMenu.exec_(self.resultsView.mapToGlobal(position)) else: contextMenu = QMenu('View Context Menu', self) contextMenu.addAction(self.loadCalculationsAction) contextMenu.addAction(self.removeAllCalculationsAction) contextMenu.exec_(self.resultsView.mapToGlobal(position)) def saveSelectedCalculationsAs(self): path, _ = QFileDialog.getSaveFileName(self, 'Save Calculations', 'untitled', 'Pickle File (*.pkl)') if path: os.chdir(os.path.dirname(path)) calculations = list(self.selectedCalculations()) calculations.reverse() with open(path, 'wb') as p: pickle.dump(calculations, p) def removeSelectedCalculations(self): indexes = self.resultsView.selectedIndexes() self.resultsModel.removeItems(indexes) def removeAllCalculations(self): self.resultsModel.reset() def loadCalculations(self): path, _ = QFileDialog.getOpenFileName(self, 'Load Calculations', '', 'Pickle File (*.pkl)') if path: with open(path, 'rb') as p: self.resultsModel.appendItems(pickle.load(p)) self.resultsView.selectionModel().setCurrentIndex( self.resultsModel.index(0, 0), QItemSelectionModel.Select) def saveInput(self): # Load the template file specific to the requested calculation. path = resourceFileName( os.path.join('modules', 'quanty', 'templates', '{}'.format(self.templateName))) try: with open(path) as p: template = p.read() except IOError as e: self.parent().statusBar().showMessage( 'Could not find template file: {}.'.format(self.templateName)) raise e self.getParameters() replacements = collections.OrderedDict() for shell in self.shells: replacements['$NElectrons_{}'.format(shell[0])] = shell[1] replacements['$T'] = self.temperature # If all components of the magnetic field are zero, # add a small contribution in the y-direction to make the simulation # converge faster. if all(value == 0.0 for value in self.magneticField): self.magneticField = (0.0, 0.00001, 0.0) replacements['$Bx'] = self.magneticField[0] replacements['$By'] = self.magneticField[1] replacements['$Bz'] = self.magneticField[2] replacements['$Emin1'] = self.energies[0][1] replacements['$Emax1'] = self.energies[0][2] replacements['$NE1'] = self.energies[0][3] # Broadening is done in the interface. value = self.e1LorentzianBroadeningDoubleSpinBox.minimum() replacements['$Gamma1'] = value if 'RIXS' in self.experiment: replacements['$Emin2'] = self.energies[1][1] replacements['$Emax2'] = self.energies[1][2] replacements['$NE2'] = self.energies[1][3] replacements['$Gamma1'] = self.energies[0][4] replacements['$Gamma2'] = self.energies[1][4] replacements['$NPsis'] = self.nPsis for term in self.hamiltonianParameters: if 'Coulomb' in term: name = 'H_coulomb' elif 'Spin-Orbit Coupling' in term: name = 'H_soc' elif 'Crystal Field' in term: name = 'H_cf' elif '3d-Ligands Hybridization' in term: name = 'H_3d_Ld_hybridization' elif '3d-4p Hybridization' in term: name = 'H_3d_4p_hybridization' configurations = self.hamiltonianParameters[term] for configuration, parameters in configurations.items(): if 'Initial' in configuration: suffix = 'i' elif 'Intermediate' in configuration: suffix = 'm' elif 'Final' in configuration: suffix = 'f' for parameter, (value, scaling) in parameters.items(): key = '${}_{}_value'.format(parameter, suffix) replacements[key] = '{}'.format(value) key = '${}_{}_scaling'.format(parameter, suffix) replacements[key] = '{}'.format(scaling) checkState = self.hamiltonianTermsCheckState[term] if checkState > 0: checkState = 1 replacements['${}'.format(name)] = checkState replacements['$baseName'] = self.baseName for replacement in replacements: template = template.replace(replacement, str(replacements[replacement])) with open(self.baseName + '.lua', 'w') as f: f.write(template) def saveInputAs(self): path, _ = QFileDialog.getSaveFileName( self, 'Save Quanty Input', '{}'.format(self.baseName + '.lua'), 'Quanty Input File (*.lua)') if path: self.baseName, _ = os.path.splitext(os.path.basename(path)) self.updateMainWindowTitle() os.chdir(os.path.dirname(path)) try: self.saveInput() except IOError: return def runCalculation(self): if 'win32' in sys.platform: self.command = 'Quanty.exe' else: self.command = 'Quanty' with open(os.devnull, 'w') as f: try: subprocess.call(self.command, stdout=f, stderr=f) except: self.parent().statusBar().showMessage( 'Could not find Quanty. Please install ' 'it and set the PATH environment variable.') return # Write the input file to disk. try: self.saveInput() except FileNotFoundError: return # You are about to run; I will give you a label, a unique identifier, # and a starting time. self.label = '{} | {} | {} | {} | {}'.format(self.element, self.charge, self.symmetry, self.experiment, self.edge) self.uuid = uuid.uuid4().hex self.startingTime = datetime.datetime.now() self.calculation = collections.OrderedDict() self.saveParameters() # Run Quanty using QProcess. self.process = QProcess() self.process.start(self.command, (self.baseName + '.lua', )) self.parent().statusBar().showMessage('Running {} {} in {}.'.format( self.command, self.baseName + '.lua', os.getcwd())) self.process.readyReadStandardOutput.connect(self.handleOutputLogging) self.process.started.connect(self.updateCalculationPushButton) self.process.finished.connect(self.processCalculation) def updateCalculationPushButton(self): icon = QIcon(resourceFileName(os.path.join('gui', 'icons', 'stop.svg'))) self.calculationPushButton.setIcon(icon) self.calculationPushButton.setText('Stop') self.calculationPushButton.setToolTip('Stop Quanty') self.calculationPushButton.disconnect() self.calculationPushButton.clicked.connect(self.stopCalculation) def resetCalculationPushButton(self): icon = QIcon(resourceFileName(os.path.join('gui', 'icons', 'play.svg'))) self.calculationPushButton.setIcon(icon) self.calculationPushButton.setText('Run') self.calculationPushButton.setToolTip('Run Quanty') self.calculationPushButton.disconnect() self.calculationPushButton.clicked.connect(self.runCalculation) def stopCalculation(self): self.process.kill() def processCalculation(self): # When did I finish? self.endingTime = datetime.datetime.now() # Reset the calculation button. self.resetCalculationPushButton() # Evaluate the exit code and status of the process. exitStatus = self.process.exitStatus() exitCode = self.process.exitCode() timeout = 10000 statusBar = self.parent().statusBar() if exitStatus == 0 and exitCode == 0: message = ('Quanty has finished successfully in ') delta = int((self.endingTime - self.startingTime).total_seconds()) hours, reminder = divmod(delta, 60) minutes, seconds = divmod(reminder, 60) if hours > 0: message += '{} hours {} minutes and {} seconds.'.format( hours, minutes, seconds) elif minutes > 0: message += '{} minutes and {} seconds.'.format(minutes, hours) else: message += '{} seconds.'.format(seconds) statusBar.showMessage(message, timeout) elif exitStatus == 0 and exitCode == 1: self.handleErrorLogging() statusBar.showMessage( ('Quanty has finished unsuccessfully. ' 'Check the logging window for more details.'), timeout) self.parent().splitter.setSizes((400, 200)) return # exitCode is platform dependend; exitStatus is always 1. elif exitStatus == 1: message = 'Quanty was stopped.' statusBar.showMessage(message, timeout) return # Copy back the details of the calculation, and overwrite all UI # changes done by the user during the calculation. self.loadParameters(self.calculation) # Initialize the spectra container. self.spectra = dict() e1Min = self.energies[0][1] e1Max = self.energies[0][2] e1NPoints = self.energies[0][3] self.spectra['e1'] = np.linspace(e1Min, e1Max, e1NPoints + 1) if 'RIXS' in self.experiment: e2Min = self.energies[1][1] e2Max = self.energies[1][2] e2NPoints = self.energies[1][3] self.spectra['e2'] = np.linspace(e2Min, e2Max, e2NPoints + 1) # Find all spectra in the current folder. pattern = '{}*.spec'.format(self.baseName) for spectrumName in glob.glob(pattern): try: spectrum = np.loadtxt(spectrumName, skiprows=5) except FileNotFoundError: return if '_iso.spec' in spectrumName: key = 'Isotropic' elif '_cd.spec' in spectrumName: key = 'Circular Dichroism' self.spectra[key] = -spectrum[:, 2::2] # Remove the spectrum file os.remove(spectrumName) self.updateSpectraComboBox() # Store the calculation details; have to encapsulate it into a list. self.saveParameters() self.resultsModel.appendItems([self.calculation]) # Update the selected item in the results view. self.resultsView.selectionModel().clearSelection() index = self.resultsModel.index(self.resultsModel.rowCount() - 1) self.resultsView.selectionModel().select(index, QItemSelectionModel.Select) # TODO: Move this action in a better place. self.parent().plotWidget.spectraComboBox.currentTextChanged.connect( self.plot) self.plot() def updateSpectraComboBox(self): self.parent().plotWidget.spectraComboBox.clear() keys = ('Isotropic', 'Circular Dichroism') for key in keys: if key in self.spectra: self.parent().plotWidget.spectraComboBox.addItem(key) self.parent().plotWidget.spectraComboBox.setCurrentIndex(0) def plot(self): if not self.spectra: return plotWidget = self.parent().plotWidget if 'RIXS' in self.experiment: plotWidget.setGraphXLabel('Incident Energy (eV)') plotWidget.setGraphYLabel('Energy Transfer (eV)') colormap = { 'name': 'viridis', 'normalization': 'linear', 'autoscale': True, 'vmin': 0.0, 'vmax': 1.0 } plotWidget.setDefaultColormap(colormap) legend = self.label + self.uuid x = self.spectra['e1'] xMin = x.min() xMax = x.max() xNPoints = x.size xScale = (xMax - xMin) / xNPoints y = self.spectra['e2'] yMin = y.min() yMax = y.max() yNPoints = y.size yScale = (yMax - yMin) / yNPoints currentPlot = plotWidget.spectraComboBox.currentText() try: z = self.spectra[currentPlot] except KeyError: return plotWidget.addImage(z, origin=(xMin, yMin), scale=(xScale, yScale)) else: plotWidget.setGraphXLabel('Absorption Energy (eV)') plotWidget.setGraphYLabel('Absorption Cross Section (a.u.)') legend = self.label + self.uuid x = self.spectra['e1'] currentPlot = plotWidget.spectraComboBox.currentText() try: y = self.spectra[currentPlot] except KeyError: return y = y[:, 0] fwhm = self.e1GaussianBroadeningDoubleSpinBox.value() if fwhm: y = self.broaden(x, y, type='gaussian', fwhm=fwhm) * y.max() fwhm = (self.e1LorentzianBroadeningDoubleSpinBox.value() - self.e1LorentzianBroadeningDoubleSpinBox.minimum()) if fwhm: y = self.broaden(x, y, type='lorentzian', fwhm=fwhm) * y.max() plotWidget.addCurve(x, y, legend) def replot(self): # Whenever the broading changes, the data related to that plot # has to change. # index = self.resultsView.selectedIndexes()[0] # self.getParameters() # self.saveParameters() # index = self.resultsModel.replaceItem(index, self.calculation) # self.resultsView.selectionModel().select( # index, QItemSelectionModel.Select) self.plot() @staticmethod def broaden(x, y, type='gaussian', fwhm=None): yb = np.zeros_like(y) if type == 'gaussian': sigma = fwhm / 2.0 * np.sqrt(2.0 * np.log(2.0)) for xi, yi in zip(x, y): yb += yi / (sigma * np.sqrt(2.0 * np.pi)) * np.exp( -1.0 / 2.0 * ((x - xi) / sigma)**2) elif type == 'lorentzian': gamma = fwhm for xi, yi in zip(x, y): yb += yi / np.pi * (0.5 * gamma) / ((x - xi)**2 + (0.5 * gamma)**2) yb = yb / yb.max() return yb def selectedHamiltonianTermChanged(self): index = self.hamiltonianTermsView.currentIndex() self.hamiltonianParametersView.setRootIndex(index) def selectedCalculations(self): indexes = self.resultsView.selectedIndexes() for index in indexes: yield self.resultsModel.getIndexData(index) def selectedCalculationsChanged(self): self.parent().plotWidget.clear() for index in self.selectedCalculations(): self.loadParameters(index) self.updateSpectraComboBox() self.setUi() self.updateMainWindowTitle() def handleOutputLogging(self): self.process.setReadChannel(QProcess.StandardOutput) data = self.process.readAllStandardOutput().data() self.parent().loggerWidget.appendPlainText(data.decode('utf-8')) def handleErrorLogging(self): self.process.setReadChannel(QProcess.StandardError) data = self.process.readAllStandardError().data() self.parent().loggerWidget.appendPlainText(data.decode('utf-8')) def updateMainWindowTitle(self): title = 'Crispy - {}'.format(self.baseName + '.lua') self.parent().setWindowTitle(title)
def load_cr3(path) -> QPixmap: """Extract the thumbnail from the image and initialize QPixmap""" process = QProcess() process.start(f"exiftool -b -JpgFromRaw {path}") process.waitForFinished() if process.exitStatus() != QProcess.NormalExit or process.exitCode() != 0: stderr = process.readAllStandardError() raise ValueError(f"Error calling exiftool: '{stderr.data().decode()}'") handler = QImageReader(process, "jpeg".encode()) handler.setAutoTransform(True) process.closeWriteChannel() process.terminate() # Extract QImage from QImageReader and convert to QPixmap pixmap = QPixmap() pixmap.convertFromImage(handler.read()) return pixmap
class SvnLogBrowserDialog(QWidget, Ui_SvnLogBrowserDialog): """ Class implementing a dialog to browse the log history. """ def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnLogBrowserDialog, self).__init__(parent) self.setupUi(self) self.__position = QPoint() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") self.filesTree.header().setSortIndicator(0, Qt.AscendingOrder) self.vcs = vcs self.__initData() self.fromDate.setDisplayFormat("yyyy-MM-dd") self.toDate.setDisplayFormat("yyyy-MM-dd") self.__resetUI() self.__messageRole = Qt.UserRole self.__changesRole = Qt.UserRole + 1 self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_sep1 = QRegExp('\\-+\\s*') self.rx_sep2 = QRegExp('=+\\s*') self.rx_rev1 = QRegExp( 'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "rev" followed by one or more decimals followed by a colon followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_rev2 = QRegExp( 'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "r" followed by one or more decimals followed by " | " followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_flags1 = QRegExp( r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") # three blanks followed by A or D or M followed by path followed by # path copied from followed by copied from revision self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') # three blanks followed by A or D or M followed by path self.flags = { 'A': self.tr('Added'), 'D': self.tr('Deleted'), 'M': self.tr('Modified'), 'R': self.tr('Replaced'), } self.intercept = False def __initData(self): """ Private method to (re-)initialize some data. """ self.__maxDate = QDate() self.__minDate = QDate() self.__filterLogsEnabled = True self.buf = [] # buffer for stdout self.diff = None self.__started = False self.__lastRev = 0 def closeEvent(self, e): """ Protected slot implementing a close event handler. @param e close event (QCloseEvent) """ if self.process is not None and \ self.process.state() != QProcess.NotRunning: self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) self.__position = self.pos() e.accept() def show(self): """ Public slot to show the dialog. """ if not self.__position.isNull(): self.move(self.__position) self.__resetUI() super(SvnLogBrowserDialog, self).show() def __resetUI(self): """ Private method to reset the user interface. """ self.fromDate.setDate(QDate.currentDate()) self.toDate.setDate(QDate.currentDate()) self.fieldCombo.setCurrentIndex( self.fieldCombo.findText(self.tr("Message"))) self.limitSpinBox.setValue( self.vcs.getPlugin().getPreferences("LogLimit")) self.stopCheckBox.setChecked( self.vcs.getPlugin().getPreferences("StopLogOnCopy")) self.logTree.clear() self.nextButton.setEnabled(True) self.limitSpinBox.setEnabled(True) def __resizeColumnsLog(self): """ Private method to resize the log tree columns. """ self.logTree.header().resizeSections(QHeaderView.ResizeToContents) self.logTree.header().setStretchLastSection(True) def __resortLog(self): """ Private method to resort the log tree. """ self.logTree.sortItems(self.logTree.sortColumn(), self.logTree.header().sortIndicatorOrder()) def __resizeColumnsFiles(self): """ Private method to resize the changed files tree columns. """ self.filesTree.header().resizeSections(QHeaderView.ResizeToContents) self.filesTree.header().setStretchLastSection(True) def __resortFiles(self): """ Private method to resort the changed files tree. """ sortColumn = self.filesTree.sortColumn() self.filesTree.sortItems(1, self.filesTree.header().sortIndicatorOrder()) self.filesTree.sortItems(sortColumn, self.filesTree.header().sortIndicatorOrder()) def __generateLogItem(self, author, date, message, revision, changedPaths): """ Private method to generate a log tree entry. @param author author info (string) @param date date info (string) @param message text of the log message (list of strings) @param revision revision info (string) @param changedPaths list of dictionary objects containing info about the changed files/directories @return reference to the generated item (QTreeWidgetItem) """ msg = [] for line in message: msg.append(line.strip()) itm = QTreeWidgetItem(self.logTree) itm.setData(0, Qt.DisplayRole, int(revision)) itm.setData(1, Qt.DisplayRole, author) itm.setData(2, Qt.DisplayRole, date) itm.setData(3, Qt.DisplayRole, " ".join(msg)) itm.setData(0, self.__messageRole, message) itm.setData(0, self.__changesRole, changedPaths) itm.setTextAlignment(0, Qt.AlignRight) itm.setTextAlignment(1, Qt.AlignLeft) itm.setTextAlignment(2, Qt.AlignLeft) itm.setTextAlignment(3, Qt.AlignLeft) itm.setTextAlignment(4, Qt.AlignLeft) try: self.__lastRev = int(revision) except ValueError: self.__lastRev = 0 return itm def __generateFileItem(self, action, path, copyFrom, copyRev): """ Private method to generate a changed files tree entry. @param action indicator for the change action ("A", "D" or "M") @param path path of the file in the repository (string) @param copyFrom path the file was copied from (None, string) @param copyRev revision the file was copied from (None, string) @return reference to the generated item (QTreeWidgetItem) """ itm = QTreeWidgetItem(self.filesTree, [ self.flags[action], path, copyFrom, copyRev, ]) itm.setTextAlignment(3, Qt.AlignRight) return itm def __getLogEntries(self, startRev=None): """ Private method to retrieve log entries from the repository. @param startRev revision number to start from (integer, string) """ self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) QApplication.processEvents() QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() self.intercept = False self.process.kill() self.buf = [] self.cancelled = False self.errors.clear() args = [] args.append('log') self.vcs.addArguments(args, self.vcs.options['global']) self.vcs.addArguments(args, self.vcs.options['log']) args.append('--verbose') args.append('--limit') args.append('{0:d}'.format(self.limitSpinBox.value())) if startRev is not None: args.append('--revision') args.append('{0}:0'.format(startRev)) if self.stopCheckBox.isChecked(): args.append('--stop-on-copy') args.append(self.fname) self.process.setWorkingDirectory(self.dname) self.inputGroup.setEnabled(True) self.inputGroup.show() self.process.start('svn', args) procStarted = self.process.waitForStarted(5000) if not procStarted: self.inputGroup.setEnabled(False) self.inputGroup.hide() E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.').format('svn')) def start(self, fn, isFile=False): """ Public slot to start the svn log command. @param fn filename to show the log for (string) @keyparam isFile flag indicating log for a file is to be shown (boolean) """ self.sbsCheckBox.setEnabled(isFile) self.sbsCheckBox.setVisible(isFile) self.errorGroup.hide() QApplication.processEvents() self.__initData() self.filename = fn self.dname, self.fname = self.vcs.splitPath(fn) self.activateWindow() self.raise_() self.logTree.clear() self.__started = True self.__getLogEntries() def __procFinished(self, exitCode, exitStatus): """ Private slot connected to the finished signal. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) """ self.__processBuffer() self.__finish() def __finish(self): """ Private slot called when the process finished or the user pressed the button. """ if self.process is not None and \ self.process.state() != QProcess.NotRunning: self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) QApplication.restoreOverrideCursor() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.inputGroup.setEnabled(False) self.inputGroup.hide() def __processBuffer(self): """ Private method to process the buffered output of the svn log command. """ noEntries = 0 log = {"message": []} changedPaths = [] for s in self.buf: if self.rx_rev1.exactMatch(s): log["revision"] = self.rx_rev.cap(1) log["author"] = self.rx_rev.cap(2) log["date"] = self.rx_rev.cap(3) # number of lines is ignored elif self.rx_rev2.exactMatch(s): log["revision"] = self.rx_rev2.cap(1) log["author"] = self.rx_rev2.cap(2) log["date"] = self.rx_rev2.cap(3) # number of lines is ignored elif self.rx_flags1.exactMatch(s): changedPaths.append({ "action": self.rx_flags1.cap(1).strip(), "path": self.rx_flags1.cap(2).strip(), "copyfrom_path": self.rx_flags1.cap(3).strip(), "copyfrom_revision": self.rx_flags1.cap(4).strip(), }) elif self.rx_flags2.exactMatch(s): changedPaths.append({ "action": self.rx_flags2.cap(1).strip(), "path": self.rx_flags2.cap(2).strip(), "copyfrom_path": "", "copyfrom_revision": "", }) elif self.rx_sep1.exactMatch(s) or self.rx_sep2.exactMatch(s): if len(log) > 1: self.__generateLogItem(log["author"], log["date"], log["message"], log["revision"], changedPaths) dt = QDate.fromString(log["date"], Qt.ISODate) if not self.__maxDate.isValid() and \ not self.__minDate.isValid(): self.__maxDate = dt self.__minDate = dt else: if self.__maxDate < dt: self.__maxDate = dt if self.__minDate > dt: self.__minDate = dt noEntries += 1 log = {"message": []} changedPaths = [] else: if s.strip().endswith(":") or not s.strip(): continue else: log["message"].append(s) self.__resizeColumnsLog() self.__resortLog() if self.__started: self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) self.__started = False if noEntries < self.limitSpinBox.value() and not self.cancelled: self.nextButton.setEnabled(False) self.limitSpinBox.setEnabled(False) self.__filterLogsEnabled = False self.fromDate.setMinimumDate(self.__minDate) self.fromDate.setMaximumDate(self.__maxDate) self.fromDate.setDate(self.__minDate) self.toDate.setMinimumDate(self.__minDate) self.toDate.setMaximumDate(self.__maxDate) self.toDate.setDate(self.__maxDate) self.__filterLogsEnabled = True self.__filterLogs() def __readStdout(self): """ Private slot to handle the readyReadStandardOutput signal. It reads the output of the process and inserts it into a buffer. """ self.process.setReadChannel(QProcess.StandardOutput) while self.process.canReadLine(): line = str(self.process.readLine(), Preferences.getSystem("IOEncoding"), 'replace') self.buf.append(line) def __readStderr(self): """ Private slot to handle the readyReadStandardError signal. It reads the error output of the process and inserts it into the error pane. """ if self.process is not None: self.errorGroup.show() s = str(self.process.readAllStandardError(), Preferences.getSystem("IOEncoding"), 'replace') self.errors.insertPlainText(s) self.errors.ensureCursorVisible() def __diffRevisions(self, rev1, rev2): """ Private method to do a diff of two revisions. @param rev1 first revision number (integer) @param rev2 second revision number (integer) """ if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked(): self.vcs.svnSbsDiff(self.filename, revisions=(str(rev1), str(rev2))) else: if self.diff is None: from .SvnDiffDialog import SvnDiffDialog self.diff = SvnDiffDialog(self.vcs) self.diff.show() self.diff.raise_() self.diff.start(self.filename, [rev1, rev2]) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @param button button that was clicked (QAbstractButton) """ if button == self.buttonBox.button(QDialogButtonBox.Close): self.close() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): self.cancelled = True self.__finish() @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) def on_logTree_currentItemChanged(self, current, previous): """ Private slot called, when the current item of the log tree changes. @param current reference to the new current item (QTreeWidgetItem) @param previous reference to the old current item (QTreeWidgetItem) """ if current is not None: self.messageEdit.clear() for line in current.data(0, self.__messageRole): self.messageEdit.append(line.strip()) self.filesTree.clear() changes = current.data(0, self.__changesRole) if len(changes) > 0: for change in changes: self.__generateFileItem(change["action"], change["path"], change["copyfrom_path"], change["copyfrom_revision"]) self.__resizeColumnsFiles() self.__resortFiles() self.diffPreviousButton.setEnabled( current != self.logTree.topLevelItem( self.logTree.topLevelItemCount() - 1)) @pyqtSlot() def on_logTree_itemSelectionChanged(self): """ Private slot called, when the selection has changed. """ self.diffRevisionsButton.setEnabled( len(self.logTree.selectedItems()) == 2) @pyqtSlot() def on_nextButton_clicked(self): """ Private slot to handle the Next button. """ if self.__lastRev > 1: self.__getLogEntries(self.__lastRev - 1) @pyqtSlot() def on_diffPreviousButton_clicked(self): """ Private slot to handle the Diff to Previous button. """ itm = self.logTree.currentItem() if itm is None: self.diffPreviousButton.setEnabled(False) return rev2 = int(itm.text(0)) itm = self.logTree.topLevelItem( self.logTree.indexOfTopLevelItem(itm) + 1) if itm is None: self.diffPreviousButton.setEnabled(False) return rev1 = int(itm.text(0)) self.__diffRevisions(rev1, rev2) @pyqtSlot() def on_diffRevisionsButton_clicked(self): """ Private slot to handle the Compare Revisions button. """ items = self.logTree.selectedItems() if len(items) != 2: self.diffRevisionsButton.setEnabled(False) return rev2 = int(items[0].text(0)) rev1 = int(items[1].text(0)) self.__diffRevisions(min(rev1, rev2), max(rev1, rev2)) @pyqtSlot(QDate) def on_fromDate_dateChanged(self, date): """ Private slot called, when the from date changes. @param date new date (QDate) """ self.__filterLogs() @pyqtSlot(QDate) def on_toDate_dateChanged(self, date): """ Private slot called, when the from date changes. @param date new date (QDate) """ self.__filterLogs() @pyqtSlot(str) def on_fieldCombo_activated(self, txt): """ Private slot called, when a new filter field is selected. @param txt text of the selected field (string) """ self.__filterLogs() @pyqtSlot(str) def on_rxEdit_textChanged(self, txt): """ Private slot called, when a filter expression is entered. @param txt filter expression (string) """ self.__filterLogs() def __filterLogs(self): """ Private method to filter the log entries. """ if self.__filterLogsEnabled: from_ = self.fromDate.date().toString("yyyy-MM-dd") to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") txt = self.fieldCombo.currentText() if txt == self.tr("Author"): fieldIndex = 1 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) elif txt == self.tr("Revision"): fieldIndex = 0 txt = self.rxEdit.text() if txt.startswith("^"): searchRx = QRegExp("^\s*{0}".format(txt[1:]), Qt.CaseInsensitive) else: searchRx = QRegExp(txt, Qt.CaseInsensitive) else: fieldIndex = 3 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) currentItem = self.logTree.currentItem() for topIndex in range(self.logTree.topLevelItemCount()): topItem = self.logTree.topLevelItem(topIndex) if topItem.text(2) <= to_ and topItem.text(2) >= from_ and \ searchRx.indexIn(topItem.text(fieldIndex)) > -1: topItem.setHidden(False) if topItem is currentItem: self.on_logTree_currentItemChanged(topItem, None) else: topItem.setHidden(True) if topItem is currentItem: self.messageEdit.clear() self.filesTree.clear() @pyqtSlot(bool) def on_stopCheckBox_clicked(self, checked): """ Private slot called, when the stop on copy/move checkbox is clicked. @param checked flag indicating the checked state (boolean) """ self.vcs.getPlugin().setPreferences("StopLogOnCopy", self.stopCheckBox.isChecked()) self.nextButton.setEnabled(True) self.limitSpinBox.setEnabled(True) def on_passwordCheckBox_toggled(self, isOn): """ Private slot to handle the password checkbox toggled. @param isOn flag indicating the status of the check box (boolean) """ if isOn: self.input.setEchoMode(QLineEdit.Password) else: self.input.setEchoMode(QLineEdit.Normal) @pyqtSlot() def on_sendButton_clicked(self): """ Private slot to send the input to the subversion process. """ input = self.input.text() input += os.linesep if self.passwordCheckBox.isChecked(): self.errors.insertPlainText(os.linesep) self.errors.ensureCursorVisible() else: self.errors.insertPlainText(input) self.errors.ensureCursorVisible() self.errorGroup.show() self.process.write(input) self.passwordCheckBox.setChecked(False) self.input.clear() def on_input_returnPressed(self): """ Private slot to handle the press of the return key in the input field. """ self.intercept = True self.on_sendButton_clicked() def keyPressEvent(self, evt): """ Protected slot to handle a key press event. @param evt the key press event (QKeyEvent) """ if self.intercept: self.intercept = False evt.accept() return super(SvnLogBrowserDialog, self).keyPressEvent(evt)
class ProjectFormsBrowser(ProjectBaseBrowser): """ A class used to display the forms part of the project. @signal appendStderr(str) emitted after something was received from a QProcess on stderr @signal sourceFile(str) emitted to open a forms file in an editor @signal uipreview(str) emitted to preview a forms file @signal trpreview(list of str) emitted to preview form files in the translations previewer @signal closeSourceWindow(str) emitted after a file has been removed/deleted from the project @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The name of the menu and a reference to the menu are given. @signal menusAboutToBeCreated() emitted when the context menus are about to be created. This is the right moment to add or remove hook methods. """ appendStderr = pyqtSignal(str) uipreview = pyqtSignal(str) showMenu = pyqtSignal(str, QMenu) menusAboutToBeCreated = pyqtSignal() PyuicIndentDefault = 4 def __init__(self, project, parent=None): """ Constructor @param project reference to the project object @param parent parent widget of this browser (QWidget) """ ProjectBaseBrowser.__init__(self, project, ProjectBrowserFormType, parent) self.selectedItemsFilter = \ [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem] self.setWindowTitle(self.tr('Forms')) self.setWhatsThis(self.tr( """<b>Project Forms Browser</b>""" """<p>This allows to easily see all forms contained in the""" """ current project. Several actions can be executed via the""" """ context menu.</p>""" )) # templates for Qt4 # these two lists have to stay in sync self.templates4 = [ 'dialog4.tmpl', 'widget4.tmpl', 'mainwindow4.tmpl', 'dialogbuttonboxbottom4.tmpl', 'dialogbuttonboxright4.tmpl', 'dialogbuttonsbottom4.tmpl', 'dialogbuttonsbottomcenter4.tmpl', 'dialogbuttonsright4.tmpl', '', 'wizard4.tmpl', 'wizardpage4.tmpl', 'qdockwidget4.tmpl', 'qframe4.tmpl', 'qgroupbox4.tmpl', 'qscrollarea4.tmpl', 'qmdiarea4.tmpl', 'qtabwidget4.tmpl', 'qtoolbox4.tmpl', 'qstackedwidget4.tmpl' ] self.templateTypes4 = [ self.tr("Dialog"), self.tr("Widget"), self.tr("Main Window"), self.tr("Dialog with Buttonbox (Bottom)"), self.tr("Dialog with Buttonbox (Right)"), self.tr("Dialog with Buttons (Bottom)"), self.tr("Dialog with Buttons (Bottom-Center)"), self.tr("Dialog with Buttons (Right)"), '', self.tr("QWizard"), self.tr("QWizardPage"), self.tr("QDockWidget"), self.tr("QFrame"), self.tr("QGroupBox"), self.tr("QScrollArea"), self.tr("QMdiArea"), self.tr("QTabWidget"), self.tr("QToolBox"), self.tr("QStackedWidget"), ] self.compileProc = None def _createPopupMenus(self): """ Protected overloaded method to generate the popup menu. """ self.menuActions = [] self.multiMenuActions = [] self.dirMenuActions = [] self.dirMultiMenuActions = [] self.menusAboutToBeCreated.emit() self.menu = QMenu(self) if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.menu.addAction( self.tr('Compile form'), self.__compileForm) self.menu.addAction( self.tr('Compile all forms'), self.__compileAllForms) self.menu.addAction( self.tr('Generate Dialog Code...'), self.__generateDialogCode) self.menu.addSeparator() self.menu.addAction( self.tr('Open in Qt-Designer'), self.__openFile) self.menu.addAction( self.tr('Open in Editor'), self.__openFileInEditor) self.menu.addSeparator() self.menu.addAction(self.tr('Preview form'), self.__UIPreview) self.menu.addAction( self.tr('Preview translations'), self.__TRPreview) else: if self.hooks["compileForm"] is not None: self.menu.addAction( self.hooksMenuEntries.get( "compileForm", self.tr('Compile form')), self.__compileForm) if self.hooks["compileAllForms"] is not None: self.menu.addAction( self.hooksMenuEntries.get( "compileAllForms", self.tr('Compile all forms')), self.__compileAllForms) if self.hooks["generateDialogCode"] is not None: self.menu.addAction( self.hooksMenuEntries.get( "generateDialogCode", self.tr('Generate Dialog Code...')), self.__generateDialogCode) if self.hooks["compileForm"] is not None or \ self.hooks["compileAllForms"] is not None or \ self.hooks["generateDialogCode"] is not None: self.menu.addSeparator() if self.hooks["open"] is not None: self.menu.addAction( self.hooksMenuEntries.get("open", self.tr('Open')), self.__openFile) self.menu.addAction(self.tr('Open'), self.__openFileInEditor) self.menu.addSeparator() act = self.menu.addAction(self.tr('Rename file'), self._renameFile) self.menuActions.append(act) act = self.menu.addAction( self.tr('Remove from project'), self._removeFile) self.menuActions.append(act) act = self.menu.addAction(self.tr('Delete'), self.__deleteFile) self.menuActions.append(act) self.menu.addSeparator() if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.menu.addAction(self.tr('New form...'), self.__newForm) else: if self.hooks["newForm"] is not None: self.menu.addAction( self.hooksMenuEntries.get( "newForm", self.tr('New form...')), self.__newForm) self.menu.addAction(self.tr('Add forms...'), self.__addFormFiles) self.menu.addAction( self.tr('Add forms directory...'), self.__addFormsDirectory) self.menu.addSeparator() self.menu.addAction( self.tr('Copy Path to Clipboard'), self._copyToClipboard) self.menu.addSeparator() self.menu.addAction( self.tr('Expand all directories'), self._expandAllDirs) self.menu.addAction( self.tr('Collapse all directories'), self._collapseAllDirs) self.menu.addSeparator() self.menu.addAction(self.tr('Configure...'), self._configure) self.backMenu = QMenu(self) if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"] or \ self.hooks["compileAllForms"] is not None: self.backMenu.addAction( self.tr('Compile all forms'), self.__compileAllForms) self.backMenu.addSeparator() self.backMenu.addAction(self.tr('New form...'), self.__newForm) else: if self.hooks["newForm"] is not None: self.backMenu.addAction( self.hooksMenuEntries.get( "newForm", self.tr('New form...')), self.__newForm) self.backMenu.addAction( self.tr('Add forms...'), self.project.addUiFiles) self.backMenu.addAction( self.tr('Add forms directory...'), self.project.addUiDir) self.backMenu.addSeparator() self.backMenu.addAction( self.tr('Expand all directories'), self._expandAllDirs) self.backMenu.addAction( self.tr('Collapse all directories'), self._collapseAllDirs) self.backMenu.addSeparator() self.backMenu.addAction(self.tr('Configure...'), self._configure) self.backMenu.setEnabled(False) # create the menu for multiple selected files self.multiMenu = QMenu(self) if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: act = self.multiMenu.addAction( self.tr('Compile forms'), self.__compileSelectedForms) self.multiMenu.addSeparator() self.multiMenu.addAction( self.tr('Open in Qt-Designer'), self.__openFile) self.multiMenu.addAction( self.tr('Open in Editor'), self.__openFileInEditor) self.multiMenu.addSeparator() self.multiMenu.addAction( self.tr('Preview translations'), self.__TRPreview) else: if self.hooks["compileSelectedForms"] is not None: act = self.multiMenu.addAction( self.hooksMenuEntries.get( "compileSelectedForms", self.tr('Compile forms')), self.__compileSelectedForms) self.multiMenu.addSeparator() if self.hooks["open"] is not None: self.multiMenu.addAction( self.hooksMenuEntries.get("open", self.tr('Open')), self.__openFile) self.multiMenu.addAction( self.tr('Open'), self.__openFileInEditor) self.multiMenu.addSeparator() act = self.multiMenu.addAction( self.tr('Remove from project'), self._removeFile) self.multiMenuActions.append(act) act = self.multiMenu.addAction( self.tr('Delete'), self.__deleteFile) self.multiMenuActions.append(act) self.multiMenu.addSeparator() self.multiMenu.addAction( self.tr('Expand all directories'), self._expandAllDirs) self.multiMenu.addAction( self.tr('Collapse all directories'), self._collapseAllDirs) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr('Configure...'), self._configure) self.dirMenu = QMenu(self) if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.dirMenu.addAction( self.tr('Compile all forms'), self.__compileAllForms) self.dirMenu.addSeparator() else: if self.hooks["compileAllForms"] is not None: self.dirMenu.addAction( self.hooksMenuEntries.get( "compileAllForms", self.tr('Compile all forms')), self.__compileAllForms) self.dirMenu.addSeparator() act = self.dirMenu.addAction( self.tr('Remove from project'), self._removeDir) self.dirMenuActions.append(act) act = self.dirMenu.addAction( self.tr('Delete'), self._deleteDirectory) self.dirMenuActions.append(act) self.dirMenu.addSeparator() if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.dirMenu.addAction(self.tr('New form...'), self.__newForm) else: if self.hooks["newForm"] is not None: self.dirMenu.addAction( self.hooksMenuEntries.get( "newForm", self.tr('New form...')), self.__newForm) self.dirMenu.addAction( self.tr('Add forms...'), self.__addFormFiles) self.dirMenu.addAction( self.tr('Add forms directory...'), self.__addFormsDirectory) self.dirMenu.addSeparator() self.dirMenu.addAction( self.tr('Copy Path to Clipboard'), self._copyToClipboard) self.dirMenu.addSeparator() self.dirMenu.addAction( self.tr('Expand all directories'), self._expandAllDirs) self.dirMenu.addAction( self.tr('Collapse all directories'), self._collapseAllDirs) self.dirMenu.addSeparator() self.dirMenu.addAction(self.tr('Configure...'), self._configure) self.dirMultiMenu = QMenu(self) if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.dirMultiMenu.addAction( self.tr('Compile all forms'), self.__compileAllForms) self.dirMultiMenu.addSeparator() else: if self.hooks["compileAllForms"] is not None: self.dirMultiMenu.addAction( self.hooksMenuEntries.get( "compileAllForms", self.tr('Compile all forms')), self.__compileAllForms) self.dirMultiMenu.addSeparator() self.dirMultiMenu.addAction( self.tr('Add forms...'), self.project.addUiFiles) self.dirMultiMenu.addAction( self.tr('Add forms directory...'), self.project.addUiDir) self.dirMultiMenu.addSeparator() self.dirMultiMenu.addAction( self.tr('Expand all directories'), self._expandAllDirs) self.dirMultiMenu.addAction( self.tr('Collapse all directories'), self._collapseAllDirs) self.dirMultiMenu.addSeparator() self.dirMultiMenu.addAction( self.tr('Configure...'), self._configure) self.menu.aboutToShow.connect(self.__showContextMenu) self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti) self.dirMenu.aboutToShow.connect(self.__showContextMenuDir) self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti) self.backMenu.aboutToShow.connect(self.__showContextMenuBack) self.mainMenu = self.menu def _contextMenuRequested(self, coord): """ Protected slot to show the context menu. @param coord the position of the mouse pointer (QPoint) """ if not self.project.isOpen(): return try: categories = self.getSelectedItemsCountCategorized( [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem]) cnt = categories["sum"] if cnt <= 1: index = self.indexAt(coord) if index.isValid(): self._selectSingleItem(index) categories = self.getSelectedItemsCountCategorized( [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem]) cnt = categories["sum"] bfcnt = categories[str(ProjectBrowserFileItem)] sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)] if cnt > 1 and cnt == bfcnt: self.multiMenu.popup(self.mapToGlobal(coord)) elif cnt > 1 and cnt == sdcnt: self.dirMultiMenu.popup(self.mapToGlobal(coord)) else: index = self.indexAt(coord) if cnt == 1 and index.isValid(): if bfcnt == 1: self.menu.popup(self.mapToGlobal(coord)) elif sdcnt == 1: self.dirMenu.popup(self.mapToGlobal(coord)) else: self.backMenu.popup(self.mapToGlobal(coord)) else: self.backMenu.popup(self.mapToGlobal(coord)) except: pass def __showContextMenu(self): """ Private slot called by the menu aboutToShow signal. """ ProjectBaseBrowser._showContextMenu(self, self.menu) self.showMenu.emit("Main", self.menu) def __showContextMenuMulti(self): """ Private slot called by the multiMenu aboutToShow signal. """ ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu) self.showMenu.emit("MainMulti", self.multiMenu) def __showContextMenuDir(self): """ Private slot called by the dirMenu aboutToShow signal. """ ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu) self.showMenu.emit("MainDir", self.dirMenu) def __showContextMenuDirMulti(self): """ Private slot called by the dirMultiMenu aboutToShow signal. """ ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu) self.showMenu.emit("MainDirMulti", self.dirMultiMenu) def __showContextMenuBack(self): """ Private slot called by the backMenu aboutToShow signal. """ ProjectBaseBrowser._showContextMenuBack(self, self.backMenu) self.showMenu.emit("MainBack", self.backMenu) def __addFormFiles(self): """ Private method to add form files to the project. """ itm = self.model().item(self.currentIndex()) if isinstance(itm, ProjectBrowserFileItem): dn = os.path.dirname(itm.fileName()) elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \ isinstance(itm, ProjectBrowserDirectoryItem): dn = itm.dirName() else: dn = None self.project.addFiles('form', dn) def __addFormsDirectory(self): """ Private method to add form files of a directory to the project. """ itm = self.model().item(self.currentIndex()) if isinstance(itm, ProjectBrowserFileItem): dn = os.path.dirname(itm.fileName()) elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \ isinstance(itm, ProjectBrowserDirectoryItem): dn = itm.dirName() else: dn = None self.project.addDirectory('form', dn) def __openFile(self): """ Private slot to handle the Open menu action. """ itmList = self.getSelectedItems() for itm in itmList[:]: try: if isinstance(itm, ProjectBrowserFileItem): # hook support if self.hooks["open"] is not None: self.hooks["open"](itm.fileName()) else: self.designerFile.emit(itm.fileName()) except: pass def __openFileInEditor(self): """ Private slot to handle the Open in Editor menu action. """ itmList = self.getSelectedItems() for itm in itmList[:]: self.sourceFile.emit(itm.fileName()) def _openItem(self): """ Protected slot to handle the open popup menu entry. """ itmList = self.getSelectedItems() for itm in itmList: if isinstance(itm, ProjectBrowserFileItem): if itm.isDesignerFile(): self.designerFile.emit(itm.fileName()) else: self.sourceFile.emit(itm.fileName()) def __UIPreview(self): """ Private slot to handle the Preview menu action. """ itmList = self.getSelectedItems() self.uipreview.emit(itmList[0].fileName()) def __TRPreview(self): """ Private slot to handle the Preview translations action. """ fileNames = [] for itm in self.getSelectedItems(): fileNames.append(itm.fileName()) trfiles = sorted(self.project.pdata["TRANSLATIONS"][:]) fileNames.extend([os.path.join(self.project.ppath, trfile) for trfile in trfiles if trfile.endswith('.qm')]) self.trpreview[list].emit(fileNames) def __newForm(self): """ Private slot to handle the New Form menu action. """ itm = self.model().item(self.currentIndex()) if itm is None: path = self.project.ppath else: try: path = os.path.dirname(itm.fileName()) except AttributeError: try: path = itm.dirName() except AttributeError: path = os.path.join(self.project.ppath, itm.data(0)) if self.hooks["newForm"] is not None: self.hooks["newForm"](path) else: if self.project.getProjectType() in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: self.__newUiForm(path) def __newUiForm(self, path): """ Private slot to handle the New Form menu action for Qt-related projects. @param path full directory path for the new form file (string) """ selectedForm, ok = QInputDialog.getItem( None, self.tr("New Form"), self.tr("Select a form type:"), self.templateTypes4, 0, False) if not ok or not selectedForm: # user pressed cancel return templateIndex = self.templateTypes4.index(selectedForm) templateFile = os.path.join( getConfig('ericTemplatesDir'), self.templates4[templateIndex]) fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.tr("New Form"), path, self.tr("Qt User-Interface Files (*.ui);;All Files (*)"), "", E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if not fname: # user aborted or didn't enter a filename return ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if os.path.exists(fname): res = E5MessageBox.yesNo( self, self.tr("New Form"), self.tr("The file already exists! Overwrite it?"), icon=E5MessageBox.Warning) if not res: # user selected to not overwrite return try: shutil.copy(templateFile, fname) except IOError as e: E5MessageBox.critical( self, self.tr("New Form"), self.tr( "<p>The new form file <b>{0}</b> could not be created.<br>" "Problem: {1}</p>").format(fname, str(e))) return self.project.appendFile(fname) self.designerFile.emit(fname) def __deleteFile(self): """ Private method to delete a form file from the project. """ itmList = self.getSelectedItems() files = [] fullNames = [] for itm in itmList: fn2 = itm.fileName() fullNames.append(fn2) fn = self.project.getRelativePath(fn2) files.append(fn) from UI.DeleteFilesConfirmationDialog import \ DeleteFilesConfirmationDialog dlg = DeleteFilesConfirmationDialog( self.parent(), self.tr("Delete forms"), self.tr( "Do you really want to delete these forms from the project?"), files) if dlg.exec_() == QDialog.Accepted: for fn2, fn in zip(fullNames, files): self.closeSourceWindow.emit(fn2) self.project.deleteFile(fn) ########################################################################### ## Methods to handle the various compile commands ########################################################################### def __readStdout(self): """ Private slot to handle the readyReadStandardOutput signal of the pyuic/rbuic process. """ if self.compileProc is None: return self.compileProc.setReadChannel(QProcess.StandardOutput) while self.compileProc and self.compileProc.canReadLine(): self.buf += str(self.compileProc.readLine(), "utf-8", 'replace') def __readStderr(self): """ Private slot to handle the readyReadStandardError signal of the pyuic/rbuic process. """ if self.compileProc is None: return ioEncoding = Preferences.getSystem("IOEncoding") self.compileProc.setReadChannel(QProcess.StandardError) while self.compileProc and self.compileProc.canReadLine(): s = self.uicompiler + ': ' error = str(self.compileProc.readLine(), ioEncoding, 'replace') s += error self.appendStderr.emit(s) def __compileUIDone(self, exitCode, exitStatus): """ Private slot to handle the finished signal of the pyuic/rbuic process. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) """ self.compileRunning = False e5App().getObject("ViewManager").enableEditorsCheckFocusIn(True) ui = e5App().getObject("UserInterface") if exitStatus == QProcess.NormalExit and exitCode == 0 and self.buf: ofn = os.path.join(self.project.ppath, self.compiledFile) try: if self.project.useSystemEol(): newline = None else: newline = self.project.getEolString() f = open(ofn, "w", encoding="utf-8", newline=newline) for line in self.buf.splitlines(): f.write(line + "\n") f.close() if self.compiledFile not in self.project.pdata["SOURCES"]: self.project.appendFile(ofn) if not self.noDialog and not ui.notificationsEnabled(): E5MessageBox.information( self, self.tr("Form Compilation"), self.tr("The compilation of the form file" " was successful.")) else: ui.showNotification( UI.PixmapCache.getPixmap("designer48.png"), self.tr("Form Compilation"), self.tr("The compilation of the form file" " was successful.")) self.project.projectFormCompiled.emit(self.compiledFile) except IOError as msg: if not self.noDialog: E5MessageBox.information( self, self.tr("Form Compilation"), self.tr( "<p>The compilation of the form file failed.</p>" "<p>Reason: {0}</p>").format(str(msg))) else: ui.showNotification( UI.PixmapCache.getPixmap("designer48.png"), self.tr("Form Compilation"), self.tr( "<p>The compilation of the form file failed.</p>" "<p>Reason: {0}</p>").format(str(msg))) else: if not self.noDialog: E5MessageBox.information( self, self.tr("Form Compilation"), self.tr("The compilation of the form file failed.")) else: ui.showNotification( UI.PixmapCache.getPixmap("designer48.png"), self.tr("Form Compilation"), self.tr("The compilation of the form file failed.")) self.compileProc = None def __compileUI(self, fn, noDialog=False, progress=None): """ Private method to compile a .ui file to a .py/.rb file. @param fn filename of the .ui file to be compiled @param noDialog flag indicating silent operations @param progress reference to the progress dialog @return reference to the compile process (QProcess) """ self.compileProc = QProcess() args = [] self.buf = "" if self.project.pdata["PROGLANGUAGE"][0] in \ ["Python", "Python2", "Python3"]: if self.project.getProjectType() in ["Qt4", "E4Plugin"]: self.uicompiler = 'pyuic4' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.bat' else: uic = self.uicompiler elif self.project.getProjectType() in ["PyQt5", "E6Plugin"]: self.uicompiler = 'pyuic5' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.bat' else: uic = self.uicompiler elif self.project.getProjectType() == "PySide": self.uicompiler = 'pyside-uic' uic = Utilities.generatePySideToolPath(self.uicompiler) else: return None elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby": if self.project.getProjectType() == "Qt4": self.uicompiler = 'rbuic4' if Utilities.isWindowsPlatform(): uic = self.uicompiler + '.exe' else: uic = self.uicompiler else: return None else: return None ofn, ext = os.path.splitext(fn) fn = os.path.join(self.project.ppath, fn) if self.project.pdata["PROGLANGUAGE"][0] in \ ["Python", "Python2", "Python3"]: dirname, filename = os.path.split(ofn) self.compiledFile = os.path.join(dirname, "Ui_" + filename + ".py") args.append("-x") indentWidth = Preferences.getQt("PyuicIndent") if indentWidth != self.PyuicIndentDefault: args.append("--indent={0}".format(indentWidth)) if Preferences.getQt("PyuicFromImports"): args.append("--from-imports") elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby": self.compiledFile = ofn + '.rb' args.append('-x') args.append(fn) self.compileProc.finished.connect(self.__compileUIDone) self.compileProc.readyReadStandardOutput.connect(self.__readStdout) self.compileProc.readyReadStandardError.connect(self.__readStderr) self.noDialog = noDialog self.compileProc.start(uic, args) procStarted = self.compileProc.waitForStarted(5000) if procStarted: self.compileRunning = True e5App().getObject("ViewManager").enableEditorsCheckFocusIn(False) return self.compileProc else: self.compileRunning = False if progress is not None: progress.cancel() E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'Could not start {0}.<br>' 'Ensure that it is in the search path.' ).format(self.uicompiler)) return None def __generateDialogCode(self): """ Private method to generate dialog code for the form (Qt4 only). """ itm = self.model().item(self.currentIndex()) fn = itm.fileName() if self.hooks["generateDialogCode"] is not None: self.hooks["generateDialogCode"](fn) else: from .CreateDialogCodeDialog import CreateDialogCodeDialog # change environment sys.path.insert(0, self.project.getProjectPath()) cwd = os.getcwd() os.chdir(os.path.dirname(os.path.abspath(fn))) dlg = CreateDialogCodeDialog(fn, self.project, self) if not dlg.initError(): dlg.exec_() # reset the environment os.chdir(cwd) del sys.path[0] def __compileForm(self): """ Private method to compile a form to a source file. """ itm = self.model().item(self.currentIndex()) fn2 = itm.fileName() fn = self.project.getRelativePath(fn2) if self.hooks["compileForm"] is not None: self.hooks["compileForm"](fn) else: self.__compileUI(fn) def __compileAllForms(self): """ Private method to compile all forms to source files. """ if self.hooks["compileAllForms"] is not None: self.hooks["compileAllForms"](self.project.pdata["FORMS"]) else: numForms = len(self.project.pdata["FORMS"]) progress = E5ProgressDialog( self.tr("Compiling forms..."), self.tr("Abort"), 0, numForms, self.tr("%v/%m Forms"), self) progress.setModal(True) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Forms")) i = 0 for fn in self.project.pdata["FORMS"]: progress.setValue(i) if progress.wasCanceled(): break proc = self.__compileUI(fn, True, progress) if proc is not None: while proc.state() == QProcess.Running: QApplication.processEvents() QThread.msleep(300) QApplication.processEvents() else: break i += 1 progress.setValue(numForms) def __compileSelectedForms(self): """ Private method to compile selected forms to source files. """ items = self.getSelectedItems() files = [self.project.getRelativePath(itm.fileName()) for itm in items] if self.hooks["compileSelectedForms"] is not None: self.hooks["compileSelectedForms"](files) else: numForms = len(files) progress = E5ProgressDialog( self.tr("Compiling forms..."), self.tr("Abort"), 0, numForms, self.tr("%v/%m Forms"), self) progress.setModal(True) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Forms")) i = 0 for fn in files: progress.setValue(i) if progress.wasCanceled(): break proc = self.__compileUI(fn, True, progress) if proc is not None: while proc.state() == QProcess.Running: QApplication.processEvents() QThread.msleep(300) QApplication.processEvents() else: break i += 1 progress.setValue(numForms) def compileChangedForms(self): """ Public method to compile all changed forms to source files. """ if self.hooks["compileChangedForms"] is not None: self.hooks["compileChangedForms"](self.project.pdata["FORMS"]) else: if self.project.getProjectType() not in \ ["Qt4", "PyQt5", "E4Plugin", "E6Plugin", "PySide"]: # ignore the request for non Qt GUI projects return progress = E5ProgressDialog( self.tr("Determining changed forms..."), None, 0, 100, self.tr("%v/%m Forms")) progress.setMinimumDuration(0) progress.setWindowTitle(self.tr("Forms")) i = 0 # get list of changed forms changedForms = [] progress.setMaximum(len(self.project.pdata["FORMS"])) for fn in self.project.pdata["FORMS"]: progress.setValue(i) QApplication.processEvents() ifn = os.path.join(self.project.ppath, fn) if self.project.pdata["PROGLANGUAGE"][0] in \ ["Python", "Python2", "Python3"]: dirname, filename = os.path.split(os.path.splitext(ifn)[0]) ofn = os.path.join(dirname, "Ui_" + filename + ".py") elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby": ofn = os.path.splitext(ifn)[0] + '.rb' if not os.path.exists(ofn) or \ os.stat(ifn).st_mtime > os.stat(ofn).st_mtime: changedForms.append(fn) i += 1 progress.setValue(i) QApplication.processEvents() if changedForms: progress.setLabelText( self.tr("Compiling changed forms...")) progress.setMaximum(len(changedForms)) i = 0 progress.setValue(i) QApplication.processEvents() for fn in changedForms: progress.setValue(i) proc = self.__compileUI(fn, True, progress) if proc is not None: while proc.state() == QProcess.Running: QApplication.processEvents() QThread.msleep(300) QApplication.processEvents() else: break i += 1 progress.setValue(len(changedForms)) QApplication.processEvents() def handlePreferencesChanged(self): """ Public slot used to handle the preferencesChanged signal. """ ProjectBaseBrowser.handlePreferencesChanged(self) ########################################################################### ## Support for hooks below ########################################################################### def _initHookMethods(self): """ Protected method to initialize the hooks dictionary. Supported hook methods are: <ul> <li>compileForm: takes filename as parameter</li> <li>compileAllForms: takes list of filenames as parameter</li> <li>compileSelectedForms: takes list of filenames as parameter</li> <li>compileChangedForms: takes list of filenames as parameter</li> <li>generateDialogCode: takes filename as parameter</li> <li>newForm: takes full directory path of new file as parameter</li> <li>open: takes a filename as parameter</li> </ul> <b>Note</b>: Filenames are relative to the project directory, if not specified differently. """ self.hooks = { "compileForm": None, "compileAllForms": None, "compileChangedForms": None, "compileSelectedForms": None, "generateDialogCode": None, "newForm": None, "open": None, }
class Process(QObject): """Abstraction over a running test subprocess process. Reads the log from its stdout and parses it. Attributes: _invalid: A list of lines which could not be parsed. _data: A list of parsed lines. _started: Whether the process was ever started. proc: The QProcess for the underlying process. exit_expected: Whether the process is expected to quit. request: The request object for the current test. Signals: ready: Emitted when the server finished starting up. new_data: Emitted when a new line was parsed. """ ready = pyqtSignal() new_data = pyqtSignal(object) KEYS = ['data'] def __init__(self, request, parent=None): super().__init__(parent) self.request = request self.captured_log = [] self._started = False self._invalid = [] self._data = [] self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardError) self.exit_expected = None # Not started at all yet def _log(self, line): """Add the given line to the captured log output.""" if self.request.config.getoption('--capture') == 'no': print(line) self.captured_log.append(line) def log_summary(self, text): """Log the given line as summary/title.""" text = '\n{line} {text} {line}\n'.format(line='=' * 30, text=text) self._log(text) def _parse_line(self, line): """Parse the given line from the log. Return: A self.ParseResult member. """ raise NotImplementedError def _executable_args(self): """Get the executable and necessary arguments as a tuple.""" raise NotImplementedError def _default_args(self): """Get the default arguments to use if none were passed to start().""" raise NotImplementedError def _get_data(self): """Get the parsed data for this test. Also waits for 0.5s to make sure any new data is received. Subprocesses are expected to alias this to a public method with a better name. """ self.proc.waitForReadyRead(500) self.read_log() return self._data def _wait_signal(self, signal, timeout=5000, raising=True): """Wait for a signal to be emitted. Should be used in a contextmanager. """ blocker = pytestqt.plugin.SignalBlocker(timeout=timeout, raising=raising) blocker.connect(signal) return blocker @pyqtSlot() def read_log(self): """Read the log from the process' stdout.""" if not hasattr(self, 'proc'): # I have no idea how this happens, but it does... return while self.proc.canReadLine(): line = self.proc.readLine() line = bytes(line).decode('utf-8', errors='ignore').rstrip('\r\n') try: parsed = self._parse_line(line) except InvalidLine: self._invalid.append(line) self._log("INVALID: {}".format(line)) continue if parsed is None: if self._invalid: self._log("IGNORED: {}".format(line)) else: self._data.append(parsed) self.new_data.emit(parsed) def start(self, args=None, *, env=None): """Start the process and wait until it started.""" self._start(args, env=env) self._started = True verbose = self.request.config.getoption('--verbose') timeout = 60 if utils.ON_CI else 20 for _ in range(timeout): with self._wait_signal(self.ready, timeout=1000, raising=False) as blocker: pass if not self.is_running(): if self.exit_expected: return # _start ensures it actually started, but it might quit shortly # afterwards raise ProcessExited( '\n' + _render_log(self.captured_log, verbose=verbose)) if blocker.signal_triggered: self._after_start() return raise WaitForTimeout("Timed out while waiting for process start.\n" + _render_log(self.captured_log, verbose=verbose)) def _start(self, args, env): """Actually start the process.""" executable, exec_args = self._executable_args() if args is None: args = self._default_args() procenv = QProcessEnvironment.systemEnvironment() if env is not None: for k, v in env.items(): procenv.insert(k, v) self.proc.readyRead.connect(self.read_log) self.proc.setProcessEnvironment(procenv) self.proc.start(executable, exec_args + args) ok = self.proc.waitForStarted() assert ok assert self.is_running() def _after_start(self): """Do things which should be done immediately after starting.""" def before_test(self): """Restart process before a test if it exited before.""" self._invalid = [] if not self.is_running(): self.start() def after_test(self): """Clean up data after each test. Also checks self._invalid so the test counts as failed if there were unexpected output lines earlier. """ __tracebackhide__ = lambda e: e.errisinstance(ProcessExited) self.captured_log = [] if self._invalid: # Wait for a bit so the full error has a chance to arrive time.sleep(1) # Exit the process to make sure we're in a defined state again self.terminate() self.clear_data() raise InvalidLine('\n' + '\n'.join(self._invalid)) self.clear_data() if not self.is_running() and not self.exit_expected and self._started: raise ProcessExited self.exit_expected = False def clear_data(self): """Clear the collected data.""" self._data.clear() def terminate(self): """Clean up and shut down the process.""" if not self.is_running(): return if quteutils.is_windows: self.proc.kill() else: self.proc.terminate() ok = self.proc.waitForFinished(5000) if not ok: cmdline = ' '.join([self.proc.program()] + self.proc.arguments()) warnings.warn( f"Test process {cmdline} with PID {self.proc.processId()} " "failed to terminate!") self.proc.kill() self.proc.waitForFinished() def is_running(self): """Check if the process is currently running.""" return self.proc.state() == QProcess.Running def _match_data(self, value, expected): """Helper for wait_for to match a given value. The behavior of this method is slightly different depending on the types of the filtered values: - If expected is None, the filter always matches. - If the value is a string or bytes object and the expected value is too, the pattern is treated as a glob pattern (with only * active). - If the value is a string or bytes object and the expected value is a compiled regex, it is used for matching. - If the value is any other type, == is used. Return: A bool """ regex_type = type(re.compile('')) if expected is None: return True elif isinstance(expected, regex_type): return expected.search(value) elif isinstance(value, (bytes, str)): return utils.pattern_match(pattern=expected, value=value) else: return value == expected def _wait_for_existing(self, override_waited_for, after, **kwargs): """Check if there are any line in the history for wait_for. Return: either the found line or None. """ for line in self._data: matches = [] for key, expected in kwargs.items(): value = getattr(line, key) matches.append(self._match_data(value, expected)) if after is None: too_early = False else: too_early = ((line.timestamp, line.msecs) < (after.timestamp, after.msecs)) if (all(matches) and (not line.waited_for or override_waited_for) and not too_early): # If we waited for this line, chances are we don't mean the # same thing the next time we use wait_for and it matches # this line again. line.waited_for = True self._log("\n----> Already found {!r} in the log: {}".format( kwargs.get('message', 'line'), line)) return line return None def _wait_for_new(self, timeout, do_skip, **kwargs): """Wait for a log message which doesn't exist yet. Called via wait_for. """ __tracebackhide__ = lambda e: e.errisinstance(WaitForTimeout) message = kwargs.get('message', None) if message is not None: elided = quteutils.elide(repr(message), 100) self._log("\n----> Waiting for {} in the log".format(elided)) spy = QSignalSpy(self.new_data) elapsed_timer = QElapsedTimer() elapsed_timer.start() while True: # Skip if there are pending messages causing a skip self._maybe_skip() got_signal = spy.wait(timeout) if not got_signal or elapsed_timer.hasExpired(timeout): msg = "Timed out after {}ms waiting for {!r}.".format( timeout, kwargs) if do_skip: pytest.skip(msg) else: raise WaitForTimeout(msg) match = self._wait_for_match(spy, kwargs) if match is not None: if message is not None: self._log("----> found it") return match raise quteutils.Unreachable def _wait_for_match(self, spy, kwargs): """Try matching the kwargs with the given QSignalSpy.""" for args in spy: assert len(args) == 1 line = args[0] matches = [] for key, expected in kwargs.items(): value = getattr(line, key) matches.append(self._match_data(value, expected)) if all(matches): # If we waited for this line, chances are we don't mean the # same thing the next time we use wait_for and it matches # this line again. line.waited_for = True return line return None def _maybe_skip(self): """Can be overridden by subclasses to skip on certain log lines. We can't run pytest.skip directly while parsing the log, as that would lead to a pytest.skip.Exception error in a virtual Qt method, which means pytest-qt fails the test. Instead, we check for skip messages periodically in QuteProc._maybe_skip, and call _maybe_skip after every parsed message in wait_for (where it's most likely that new messages arrive). """ def wait_for(self, timeout=None, *, override_waited_for=False, do_skip=False, divisor=1, after=None, **kwargs): """Wait until a given value is found in the data. Keyword arguments to this function get interpreted as attributes of the searched data. Every given argument is treated as a pattern which the attribute has to match against. Args: timeout: How long to wait for the message. override_waited_for: If set, gets triggered by previous messages again. do_skip: If set, call pytest.skip on a timeout. divisor: A factor to decrease the timeout by. after: If it's an existing line, ensure it's after the given one. Return: The matched line. """ __tracebackhide__ = lambda e: e.errisinstance(WaitForTimeout) if timeout is None: if do_skip: timeout = 2000 elif utils.ON_CI: timeout = 15000 else: timeout = 5000 timeout //= divisor if not kwargs: raise TypeError("No keyword arguments given!") for key in kwargs: assert key in self.KEYS existing = self._wait_for_existing(override_waited_for, after, **kwargs) if existing is not None: return existing else: return self._wait_for_new(timeout=timeout, do_skip=do_skip, **kwargs) def ensure_not_logged(self, delay=500, **kwargs): """Make sure the data matching the given arguments is not logged. If nothing is found in the log, we wait for delay ms to make sure nothing arrives. """ __tracebackhide__ = lambda e: e.errisinstance(BlacklistedMessageError) try: line = self.wait_for(timeout=delay, override_waited_for=True, **kwargs) except WaitForTimeout: return else: raise BlacklistedMessageError(line) def wait_for_quit(self): """Wait until the process has quit.""" self.exit_expected = True with self._wait_signal(self.proc.finished, timeout=15000): pass
def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnLogBrowserDialog, self).__init__(parent) self.setupUi(self) self.__position = QPoint() self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow.png")) self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow.png")) self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") self.filesTree.header().setSortIndicator(0, Qt.AscendingOrder) self.vcs = vcs self.__initData() self.fromDate.setDisplayFormat("yyyy-MM-dd") self.toDate.setDisplayFormat("yyyy-MM-dd") self.__resetUI() self.__messageRole = Qt.UserRole self.__changesRole = Qt.UserRole + 1 self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_sep1 = QRegExp('\\-+\\s*') self.rx_sep2 = QRegExp('=+\\s*') self.rx_rev1 = QRegExp( r'rev ([0-9]+): ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "rev" followed by one or more decimals followed by a colon followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_rev2 = QRegExp( r'r([0-9]+) \| ([^|]*) \| ([^|]*) \| ([0-9]+) .*') # "r" followed by one or more decimals followed by " | " followed # anything up to " | " (twice) followed by one or more decimals # followed by anything self.rx_flags1 = QRegExp( r""" ([ADM])\s(.*)\s+\(\w+\s+(.*):([0-9]+)\)\s*""") # three blanks followed by A or D or M followed by path followed by # path copied from followed by copied from revision self.rx_flags2 = QRegExp(' ([ADM]) (.*)\\s*') # three blanks followed by A or D or M followed by path self.flags = { 'A': self.tr('Added'), 'D': self.tr('Deleted'), 'M': self.tr('Modified'), 'R': self.tr('Replaced'), } self.intercept = False self.__logTreeNormalFont = self.logTree.font() self.__logTreeNormalFont.setBold(False) self.__logTreeBoldFont = self.logTree.font() self.__logTreeBoldFont.setBold(True) self.__finishCallbacks = []
class csodiaqWindow(QWidget): def __init__(self): super().__init__() self.p = None self.dict = None self.INVALID = -1 self.VALID_COLOR = 'lightgreen' self.INVALID_COLOR = 'rgb(255,99,71)' dlgLayout = QVBoxLayout() fileLayout = QFormLayout() settingLayout = QFormLayout() self.set_files_parent(fileLayout) self.set_files_child(fileLayout) dlgLayout.addWidget(QLabel('File Input:')) dlgLayout.addLayout(fileLayout) self.set_settings_parent(settingLayout) self.set_settings_child(settingLayout) dlgLayout.addWidget(QLabel('Settings:')) dlgLayout.addLayout(settingLayout) self.runBtn = QPushButton('Execute') self.killBtn = QPushButton('Kill Process') self.text = QPlainTextEdit() self.text.setReadOnly(True) dlgLayout.addWidget(self.runBtn) dlgLayout.addWidget(self.killBtn) dlgLayout.addWidget(self.text) self.runBtn.setDefault(True) self.killBtn.setEnabled(False) self.killBtn.clicked.connect(self.kill_process) self.runBtn.clicked.connect(self.start_process) self.setLayout(dlgLayout) def set_files_parent(self, fileLayout): self.diaFiles = QListWidget() self.delBtn = QPushButton('Delete') self.delBtn.clicked.connect(self.delete_selected_values) self.clrBtn = QPushButton('Clear') self.clrBtn.clicked.connect(self.diaFiles.clear) self.diaFileText = QLabel('DIA Data File:') self.diaBtn = QPushButton('Browse') self.diaBtn.clicked.connect(lambda: self.getfile(self.diaFiles)) self.diaFileText.setToolTip( 'Data File Requirements:\n-Required, must be populated\n-Must be of type MzXML' ) self.libFileText = QLabel('Library File:') self.libBtn = QPushButton('Browse') self.libBtn.clicked.connect(lambda: self.getfile(self.libFile)) self.libFileText.setToolTip( 'Library Spectra File Requirements:\n-Required, must be populated\n-Must be in MGF (.mgf) or TraML (.csv or .tsv) format' ) self.libFile = QLineEdit() self.outDirText = QLabel('Outfile Directory:') self.dirBtn = QPushButton('Browse') self.outDirText.setToolTip( 'Data File Requirements:\n-Required, must be populated') self.outDir = QLineEdit() self.dirBtn.clicked.connect( lambda: self.getfile(self.outDir, isFile=False)) fileLayout.addRow(self.diaFileText, self.diaBtn) fileLayout.addRow(self.diaFiles) fileLayout.addRow(self.delBtn, self.clrBtn) fileLayout.addRow(self.libFileText, self.libBtn) fileLayout.addRow(self.libFile) fileLayout.addRow(self.outDirText, self.dirBtn) fileLayout.addRow(self.outDir) def set_files_child(self, fileLayout): pass def set_settings_parent(self, settingLayout): self.fragMassTolText = QLabel( 'Initial Fragment Mass Tolerance (in ppm):') self.fragMassTolText.setToolTip( 'Fragment Mass Tolerance Requirements:\n-Must be blank or an integer greater than 0' ) self.fragMassTol = QLineEdit() self.corr = QLineEdit() self.corrCheckBox = QCheckBox() self.corrText = QLabel('Corrective Standard Deviations:') self.corrText.setToolTip( 'Corrective Standard Deviations Requirements:\n-Must be blank or a float (decimal value) between or equal to 0.5 and 2.0' ) self.histCheckBox = QCheckBox() self.corrCheckBox.stateChanged.connect( lambda: self.check_grey(self.corrCheckBox, self.corr)) self.corrCheckBox.stateChanged.connect(lambda: self.enable_second_box( self.corrCheckBox, self.histCheckBox)) settingLayout.addRow(self.fragMassTolText, self.fragMassTol) settingLayout.addRow('Correction:', self.corrCheckBox) settingLayout.addRow(self.corrText, self.corr) settingLayout.addRow('Create Histogram:', self.histCheckBox) self.fragMassTol.setPlaceholderText('30') self.corr.setPlaceholderText('customized') self.corrCheckBox.setCheckState(2) self.libFile.setEnabled(False) self.outDir.setEnabled(False) self.histCheckBox.setCheckState(2) def set_settings_child(self, settingLayout): pass def check_grey(self, checkBox, lineEdit, filledText=''): if checkBox.isChecked(): lineEdit.setText(filledText) lineEdit.setEnabled(True) else: lineEdit.clear() lineEdit.setEnabled(False) def enable_second_box(self, checkBox1, checkBox2): if checkBox1.isChecked(): checkBox2.setEnabled(True) else: checkBox2.setCheckState(False) checkBox2.setEnabled(False) def getfile(self, text, isFile=True): dialog = QFileDialog() if type(text) is QListWidget and isFile: fname = QFileDialog.getOpenFileNames(self, 'Open File', 'c:\\') text.addItems([x for x in fname[0]]) elif type(text) is QLineEdit and isFile: fname = QFileDialog.getOpenFileName(self, 'Open File', 'c:\\') text.setText(fname[0]) else: fname = QFileDialog.getExistingDirectory(self, 'Open Directory', 'c:\\') text.setText(fname) def delete_selected_values(self): for x in self.diaFiles.selectedItems(): self.diaFiles.takeItem(self.diaFiles.row(x)) def set_dict(self): tempDict = {} self.set_dict_parent(tempDict) self.set_dict_child(tempDict) if -1 in list(tempDict.values()): self.dict = None return else: self.dict = tempDict def set_dict_parent(self, tempDict): tempDict['diaFiles'] = self.return_dia_file_values( self.diaFiles, self.diaFileText, permittedTypes=['mzxml']) tempDict['libFile'] = self.return_file_path_value( self.libFile.text(), self.libFileText, permittedTypes=['mgf', 'csv', 'tsv']) tempDict['outDir'] = self.return_file_path_value( self.outDir.text(), self.outDirText) tempDict['fragMassTol'] = self.return_integer_above_0( self.fragMassTol.text(), self.fragMassTolText) if self.corrCheckBox.isChecked(): tempDict['corr'] = self.return_float_between_P5_2( self.corr.text(), self.corrText) else: tempDict['corr'] = self.return_valid(self.corrText, False) if self.histCheckBox.isChecked(): tempDict['hist'] = True else: tempDict['hist'] = False def set_dict_child(self, tempDict): pass def return_dia_file_values(self, filesObject, text, permittedTypes=[]): if filesObject.count() == 0: return self.return_valid(text) files = [ filesObject.item(i).text() for i in range(filesObject.count()) ] if len(permittedTypes) != 0: for file in files: if file.split('.')[-1].lower() not in permittedTypes: return self.return_valid(text) return self.return_valid(text, files) def return_file_path_value(self, path, text, permittedTypes=[]): if len(path) == 0: return self.return_valid(text) if len(permittedTypes) != 0 and path.split( '.')[-1].lower() not in permittedTypes: return self.return_valid(text) return self.return_valid(text, path) def return_integer_above_0(self, x, text): if x == '': return self.return_valid(text, 'default') try: v = int(x) if v < 1: return self.return_valid(text) return self.return_valid(text, x) except ValueError: return self.return_valid(text) def return_float_between_P5_2(self, x, text): if x == '': return self.return_valid(text, 'default') try: v = float(x) if v < 0.5 or v > 2.0: return self.return_valid(text) return self.return_valid(text, x) except ValueError: return self.return_valid(text) def return_valid(self, text, x=-1): if x == -1: text.setStyleSheet('background-color: ' + self.INVALID_COLOR) return -1 else: text.setStyleSheet('background-color: ' + self.VALID_COLOR) return x def message(self, s): self.text.appendPlainText(s) def start_process(self): self.set_dict() if self.dict == None: return self.killBtn.setEnabled(True) if self.p is None: # No process running. args = [] self.set_args_parent(args) self.set_args_child(args) self.message("Executing process") self.p = QProcess( ) # Keep a reference to the QProcess (e.g. on self) while it's running. self.p.readyReadStandardOutput.connect(self.handle_stdout) self.p.readyReadStandardError.connect(self.handle_stderr) self.p.finished.connect( self.process_finished) # Clean up once complete. self.p.start('csodiaq', args) def set_args_parent(self, args): for file in self.dict['diaFiles']: args += ['-f', file] args += ['-l', self.dict['libFile']] args += ['-o', self.dict['outDir'] + '/'] if self.dict['fragMassTol'] != 'default': args += ['-t', self.dict['fragMassTol']] if self.dict['corr'] == 'default': args += ['-c'] elif self.dict['corr']: args += ['-c', self.dict['corr']] if self.dict['hist']: args += ['-hist'] def set_args_child(self, args): pass def handle_stderr(self): data = self.p.readAllStandardError() stderr = bytes(data).decode("utf8") self.message(stderr) def handle_stdout(self): data = self.p.readAllStandardOutput() stdout = bytes(data).decode("utf8") self.message(stdout) def kill_process(self): self.p.kill() def process_finished(self): self.message("Process finished.") self.killBtn.setEnabled(False) self.p = None
class SvnTagBranchListDialog(QDialog, Ui_SvnTagBranchListDialog): """ Class implementing a dialog to show a list of tags or branches. """ def __init__(self, vcs, parent=None): """ Constructor @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super(SvnTagBranchListDialog, self).__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.Window) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) self.vcs = vcs self.tagsList = None self.allTagsList = None self.tagList.headerItem().setText(self.tagList.columnCount(), "") self.tagList.header().setSortIndicator(3, Qt.AscendingOrder) self.process = QProcess() self.process.finished.connect(self.__procFinished) self.process.readyReadStandardOutput.connect(self.__readStdout) self.process.readyReadStandardError.connect(self.__readStderr) self.rx_list = QRegExp( r"""\w*\s*(\d+)\s+(\w+)\s+\d*\s*""" r"""((?:\w+\s+\d+|[0-9.]+\s+\w+)\s+[0-9:]+)\s+(.+)/\s*""") def closeEvent(self, e): """ Protected slot implementing a close event handler. @param e close event (QCloseEvent) """ if (self.process is not None and self.process.state() != QProcess.NotRunning): self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) e.accept() def start(self, path, tags, tagsList, allTagsList): """ Public slot to start the svn status command. @param path name of directory to be listed (string) @param tags flag indicating a list of tags is requested (False = branches, True = tags) @param tagsList reference to string list receiving the tags (list of strings) @param allTagsList reference to string list all tags (list of strings) """ self.errorGroup.hide() self.tagList.clear() self.intercept = False if not tags: self.setWindowTitle(self.tr("Subversion Branches List")) self.activateWindow() self.tagsList = tagsList self.allTagsList = allTagsList dname, fname = self.vcs.splitPath(path) self.process.kill() reposURL = self.vcs.svnGetReposName(dname) if reposURL is None: E5MessageBox.critical( self, self.tr("Subversion Error"), self.tr( """The URL of the project repository could not be""" """ retrieved from the working copy. The list operation""" """ will be aborted""")) self.close() return args = [] args.append('list') self.vcs.addArguments(args, self.vcs.options['global']) args.append('--verbose') if self.vcs.otherData["standardLayout"]: # determine the base path of the project in the repository rx_base = QRegExp('(.+)/(trunk|tags|branches).*') if not rx_base.exactMatch(reposURL): E5MessageBox.critical( self, self.tr("Subversion Error"), self.tr("""The URL of the project repository has an""" """ invalid format. The list operation will""" """ be aborted""")) return reposRoot = rx_base.cap(1) if tags: args.append("{0}/tags".format(reposRoot)) else: args.append("{0}/branches".format(reposRoot)) self.path = None else: reposPath, ok = QInputDialog.getText( self, self.tr("Subversion List"), self.tr("Enter the repository URL containing the tags" " or branches"), QLineEdit.Normal, self.vcs.svnNormalizeURL(reposURL)) if not ok: self.close() return if not reposPath: E5MessageBox.critical( self, self.tr("Subversion List"), self.tr("""The repository URL is empty.""" """ Aborting...""")) self.close() return args.append(reposPath) self.path = reposPath self.process.setWorkingDirectory(dname) self.process.start('svn', args) procStarted = self.process.waitForStarted(5000) if not procStarted: self.inputGroup.setEnabled(False) self.inputGroup.hide() E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.').format('svn')) else: self.inputGroup.setEnabled(True) self.inputGroup.show() def __finish(self): """ Private slot called when the process finished or the user pressed the button. """ if (self.process is not None and self.process.state() != QProcess.NotRunning): self.process.terminate() QTimer.singleShot(2000, self.process.kill) self.process.waitForFinished(3000) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.buttonBox.button(QDialogButtonBox.Close).setFocus( Qt.OtherFocusReason) self.inputGroup.setEnabled(False) self.inputGroup.hide() self.__resizeColumns() self.__resort() def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @param button button that was clicked (QAbstractButton) """ if button == self.buttonBox.button(QDialogButtonBox.Close): self.close() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): self.__finish() def __procFinished(self, exitCode, exitStatus): """ Private slot connected to the finished signal. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) """ self.__finish() def __resort(self): """ Private method to resort the tree. """ self.tagList.sortItems(self.tagList.sortColumn(), self.tagList.header().sortIndicatorOrder()) def __resizeColumns(self): """ Private method to resize the list columns. """ self.tagList.header().resizeSections(QHeaderView.ResizeToContents) self.tagList.header().setStretchLastSection(True) def __generateItem(self, revision, author, date, name): """ Private method to generate a tag item in the taglist. @param revision revision string (string) @param author author of the tag (string) @param date date of the tag (string) @param name name (path) of the tag (string) """ itm = QTreeWidgetItem(self.tagList) itm.setData(0, Qt.DisplayRole, int(revision)) itm.setData(1, Qt.DisplayRole, author) itm.setData(2, Qt.DisplayRole, date) itm.setData(3, Qt.DisplayRole, name) itm.setTextAlignment(0, Qt.AlignRight) def __readStdout(self): """ Private slot to handle the readyReadStdout signal. It reads the output of the process, formats it and inserts it into the contents pane. """ self.process.setReadChannel(QProcess.StandardOutput) while self.process.canReadLine(): s = str(self.process.readLine(), Preferences.getSystem("IOEncoding"), 'replace') if self.rx_list.exactMatch(s): rev = "{0:6}".format(self.rx_list.cap(1)) author = self.rx_list.cap(2) date = self.rx_list.cap(3) path = self.rx_list.cap(4) if path == ".": continue self.__generateItem(rev, author, date, path) if not self.vcs.otherData["standardLayout"]: path = self.path + '/' + path if self.tagsList is not None: self.tagsList.append(path) if self.allTagsList is not None: self.allTagsList.append(path) def __readStderr(self): """ Private slot to handle the readyReadStderr signal. It reads the error output of the process and inserts it into the error pane. """ if self.process is not None: self.errorGroup.show() s = str(self.process.readAllStandardError(), Preferences.getSystem("IOEncoding"), 'replace') self.errors.insertPlainText(s) self.errors.ensureCursorVisible() def on_passwordCheckBox_toggled(self, isOn): """ Private slot to handle the password checkbox toggled. @param isOn flag indicating the status of the check box (boolean) """ if isOn: self.input.setEchoMode(QLineEdit.Password) else: self.input.setEchoMode(QLineEdit.Normal) @pyqtSlot() def on_sendButton_clicked(self): """ Private slot to send the input to the subversion process. """ inputTxt = self.input.text() inputTxt += os.linesep if self.passwordCheckBox.isChecked(): self.errors.insertPlainText(os.linesep) self.errors.ensureCursorVisible() else: self.errors.insertPlainText(inputTxt) self.errors.ensureCursorVisible() self.process.write(strToQByteArray(inputTxt)) self.passwordCheckBox.setChecked(False) self.input.clear() def on_input_returnPressed(self): """ Private slot to handle the press of the return key in the input field. """ self.intercept = True self.on_sendButton_clicked() def keyPressEvent(self, evt): """ Protected slot to handle a key press event. @param evt the key press event (QKeyEvent) """ if self.intercept: self.intercept = False evt.accept() return super(SvnTagBranchListDialog, self).keyPressEvent(evt)
class SSHAddWindow(SSHAddBase, SSHAddUI): def __init__(self): super().__init__() self.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.closeButton.clicked.connect(self.accept) self.generateButton.clicked.connect(self.generate_key) self.init_format() self.init_length() def init_format(self): self.formatSelect.addItem(self.tr('ED25519 (Recommended)'), 'ed25519') self.formatSelect.addItem(self.tr('RSA (Legacy)'), 'rsa') self.formatSelect.addItem(self.tr('ECDSA'), 'ecdsa') self.outputFileTextBox.setText('~/.ssh/id_ed25519') self.formatSelect.currentIndexChanged.connect( self.format_select_change) def format_select_change(self, index): new_output = f'~/.ssh/id_{self.formatSelect.currentData()}' self.outputFileTextBox.setText(new_output) def init_length(self): self.lengthSelect.addItem(self.tr('High (Recommended)'), ('4096', '521')) self.lengthSelect.addItem(self.tr('Medium'), ('2048', '384')) def generate_key(self): format = self.formatSelect.currentData() length = self.lengthSelect.currentData() if format == 'rsa': length = length[0] else: length = length[1] output_path = os.path.expanduser(self.outputFileTextBox.text()) if os.path.isfile(output_path): self.errors.setText( self.tr('Key file already exists. Not overwriting.')) else: self.sshproc = QProcess(self) self.sshproc.finished.connect(self.generate_key_result) self.sshproc.start( 'ssh-keygen', ['-t', format, '-b', length, '-f', output_path, '-N', '']) def generate_key_result(self, exitCode, exitStatus): if exitCode == 0: output_path = os.path.expanduser(self.outputFileTextBox.text()) pub_key = open(output_path + '.pub').read().strip() clipboard = QApplication.clipboard() clipboard.setText(pub_key) self.errors.setText( self.tr('New key was copied to clipboard and written to %s.') % output_path) else: self.errors.setText(self.tr('Error during key generation.')) def get_values(self): return { 'ssh_key': self.sshComboBox.currentData(), 'encryption': self.encryptionComboBox.currentData(), 'repo_url': self.repoURL.text(), 'password': self.passwordLineEdit.text() }
class MainWindow(QMainWindow): def __init__(self, path_to_conf, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) icon = QIcon( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.png')) self.setWindowIcon(icon) self.port = 0 self.path_to_conf = path_to_conf self.load_conf(path_to_conf) self.tray = SystemTrayIcon(icon, self) self.tray.show() self.consoleText = deque(maxlen=300) self.runner = QProcess(self) self.refresh_op = [] # log self.ui.console.setWordWrapMode(3) # local rules self.ui.AddLocalRuleButton.clicked.connect(self.addLocalRule) self.ui.isgfwedTestButton.clicked.connect(self.isgfwedTest) self.spacer_LR = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.ui.LocalRulesLayout.addItem(self.spacer_LR) self.local_rule_list = [] # redir rules self.ui.AddRedirectorRuleButton.clicked.connect(self.addRedirRule) self.spacer_RR = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.ui.RedirectorRulesLayout.addItem(self.spacer_RR) self.redir_rule_list = [] # proxyList self.ui.proxyAddButton.clicked.connect(self.addProxy) self.ui.proxyRemoveButton.clicked.connect(self.delProxy) self.ui.protocolBox.currentIndexChanged.connect(self.protocolChanged) self.ui.proxyDisableButton.clicked.connect(self.disableProxy) self.ui.proxyActivateButton.clicked.connect(self.activateProxy) self.ui.exclusiveProxyAddButton.clicked.connect(self.exclusiveProxyAdd) self.ui.hostnameEdit.textChanged.connect(self.proxy_hostname_changed) header = [ _tr("MainWindow", "name"), _tr("MainWindow", "address"), _tr("MainWindow", "priority"), _tr("MainWindow", "resp"), ] data = [] self.PL_table_model = MyTableModel(self, data, header) self.ui.proxyListView.setModel(self.PL_table_model) self.ui.proxyListView.pressed.connect(self.on_proxy_select) import hxcrypto self.method_list = [''] self.method_list.extend( sorted(sorted(hxcrypto.method_supported.keys()), key=lambda x: hxcrypto.is_aead(x))) self.ui.encryptionBox.addItems(self.method_list) self.ui.protocolBox.addItems(SUPPORTED_PROTOCOL) # port forward self.ui.PFAddButton.clicked.connect(self.addForward) self.ui.PFRemoveButton.clicked.connect(self.delForward) header = [ _tr("MainWindow", "target"), _tr("MainWindow", "proxy"), _tr("MainWindow", "port"), ] data = [] self.PF_table_model = MyTableModel(self, data, header) self.ui.PFView.setModel(self.PF_table_model) # settings self.ui.gfwlistToggle.stateChanged.connect(self.gfwlistToggle) self.ui.adblockToggle.stateChanged.connect(self.adblockToggle) self.ui.editConfButton.clicked.connect(self.openconf) self.ui.editLocalButton.clicked.connect(self.openlocal) self.ui.sys_proxy_toggle.setCheckState( QtCore.Qt.Checked if self.ieproxy else QtCore.Qt.Unchecked) self.ui.sys_proxy_toggle.stateChanged.connect(self.sysProxyToggle) self.ui.startup_toggle.stateChanged.connect(self.startup_toggle) if not sys.platform.startswith('win'): self.ui.sys_proxy_toggle.hide() self.ui.startup_toggle.hide() else: from .startup import startup_status self.ui.startup_toggle.setCheckState( QtCore.Qt.Checked if startup_status() else QtCore.Qt.Unchecked) self.createProcess() def load_conf(self, path_to_conf): self.path_to_conf = path_to_conf self.path_to_local = os.path.join( os.path.dirname(os.path.abspath(self.path_to_conf)), 'local.txt') self.conf = configparser.ConfigParser(interpolation=None) self.conf.optionxform = str self.conf.read(self.path_to_conf) listen = self.conf['FWLite'].get('listen', '8118') if not listen: listen = '8118' port = int(listen) if listen.isdigit() else int(listen.split(':')[1]) self.ieproxy = self.conf['FWLite'].getboolean('ieproxy', True) if port != self.port: self.port = port if sys.platform.startswith('win') and self.ieproxy: setIEproxy(1, u'127.0.0.1:%d' % self.port) _pass = self.conf['FWLite'].get('remotepass', None) if _pass: _pass = '******' + _pass self.api_auth = { 'Authorization': 'Basic %s' % base64.b64encode(_pass.encode()).decode() } if _pass else {} # load plugin from config file for plugin_name in SUPPORTED_PLUGIN: if plugin_name: SUPPORTED_PLUGIN.remove(plugin_name) for plugin_name, _ in self.conf.items('plugin'): if plugin_name not in SUPPORTED_PLUGIN: SUPPORTED_PLUGIN.append(plugin_name) self.ui.pluginBox.clear() self.ui.pluginBox.addItems(SUPPORTED_PLUGIN) def addForward(self): try: target = self.ui.PFTargetEdit.text() port = self.ui.PFPortEdit.text() if not port.isdigit(): port = 0 port = int(port) proxy = self.ui.PFProxyBox.currentText() data = json.dumps((target, proxy, port)).encode() req = Request('http://127.0.0.1:%d/api/forward' % self.port, data, headers=self.api_auth) urlopen(req, timeout=1).read() self.ui.PFTargetEdit.clear() self.ui.PFPortEdit.clear() except Exception as e: print(repr(e)) print(traceback.format_exc()) def delForward(self): index = self.ui.PFView.currentIndex().row() try: port = self.PF_table_model.mylist[index][2] req = Request('http://127.0.0.1:%d/api/forward/%s' % (self.port, port), headers=self.api_auth, method='DELETE') urlopen(req, timeout=1).read() except Exception as e: print(repr(e)) print(traceback.format_exc()) def refresh_forwardList(self): try: req = Request('http://127.0.0.1:%d/api/forward' % self.port, headers=self.api_auth) data = json.loads(urlopen(req, timeout=1).read().decode()) self.PF_table_model.update(data) self.ui.PFView.resizeRowsToContents() self.ui.PFView.resizeColumnsToContents() except Exception as e: print(repr(e)) print(traceback.format_exc()) def refresh_proxyList(self): try: req = Request('http://127.0.0.1:%d/api/proxy' % self.port, headers=self.api_auth) data = json.loads(urlopen(req, timeout=1).read().decode()) self.PL_table_model.update(data) self.ui.proxyListView.resizeRowsToContents() self.ui.proxyListView.resizeColumnsToContents() # update PFProxyBox self.ui.PFProxyBox.clear() proxy_list = [item[0] for item in data] self.ui.PFProxyBox.addItems(proxy_list) self.tray.resolve.set_proxy(proxy_list) except Exception as e: print(repr(e)) print(traceback.format_exc()) def refresh_Settings(self): try: req = Request('http://127.0.0.1:%d/api/gfwlist' % self.port, headers=self.api_auth) self.ui.gfwlistToggle.setCheckState( QtCore.Qt.Checked if json. loads(urlopen(req, timeout=1).read().decode() ) else QtCore.Qt.Unchecked) req = Request('http://127.0.0.1:%d/api/adblock' % self.port, headers=self.api_auth) self.ui.adblockToggle.setCheckState( QtCore.Qt.Checked if json. loads(urlopen(req, timeout=1).read().decode() ) else QtCore.Qt.Unchecked) except Exception as e: print(repr(e)) print(traceback.format_exc()) def exclusiveProxyAdd(self): name = self.ui.nameEdit.text() self.addProxy(enable=True) # disable all other proxy name_list = [item[0] for item in self.PL_table_model.mylist] for _name in name_list: if _name == name: continue if _name.startswith('FWLITE:'): continue self.load_proxy_by_name(_name) self.ui.priorityEdit.setText(str(-1)) self.addProxy() def addProxy(self, enable=False): protocol = self.ui.protocolBox.currentText() name = self.ui.nameEdit.text() hostname = self.ui.hostnameEdit.text() port = self.ui.portEdit.text() encryption = self.ui.encryptionBox.currentText() psk = self.ui.pskEdit.text() priority = self.ui.priorityEdit.text() username = self.ui.usernameEdit.text() password = self.ui.passwordEdit.text() plugin = self.ui.pluginBox.currentText() plugin_opt = self.ui.plugin_optEdit.text() via = self.ui.viaEdit.text() if not port.isdigit(): return if protocol == 'shadowsocks': protocol = 'ss' elif protocol == 'hxsocks2': protocol = 'hxs2' if not name: name = '%s-%s' % (hostname, port) if not priority: priority = 99 priority = int(float(priority)) if enable and priority < 0: priority = 99 # if not all([hostname, port.isdigit(), encryption, psk]): # self.tray.showMessage_(_tr("MainWindow", "error_notice")) # return qs = {} urlquote = urllib.parse.quote if protocol == 'ss': userinfo = '%s:%s' % (encryption, psk) userinfo = base64.b64encode(userinfo.encode()).decode() url = 'ss://%s@%s:%s/' % (userinfo, hostname, port) else: if username: url = '%s://%s:%s@%s:%s/' % ( protocol, username, urlquote(password), hostname, port) else: url = '%s://%s:%s/' % (protocol, hostname, port) if protocol == 'hxs2': qs['PSK'] = urlquote(psk) qs['method'] = encryption if plugin: if plugin_opt: plugin_info = urlquote(plugin + ';' + plugin_opt) else: plugin_info = urlquote(plugin) qs['plugin'] = plugin_info if qs: query_string = '&'.join(['%s=%s' % (k, v) for k, v in qs.items()]) url += '?' + query_string if via: url += '|' url += via url += ' %s' % priority data = json.dumps((name, url)).encode() try: req = Request('http://127.0.0.1:%d/api/proxy' % self.port, data, headers=self.api_auth) urlopen(req, timeout=1).read() except Exception: self.statusBar().showMessage('add proxy %s failed!' % name, 3000) else: self.ui.nameEdit.clear() self.ui.hostnameEdit.clear() self.ui.portEdit.clear() self.ui.pskEdit.clear() self.ui.usernameEdit.clear() self.ui.passwordEdit.clear() self.ui.plugin_optEdit.clear() self.ui.priorityEdit.clear() self.ui.viaEdit.clear() def protocolChanged(self): ps = self.ui.protocolBox.currentText() self.ui.usernameEdit.setEnabled(ps != 'shadowsocks') self.ui.passwordEdit.setEnabled(ps != 'shadowsocks') self.ui.encryptionBox.setEnabled(ps in ('shadowsocks', 'hxsocks2')) self.ui.pskEdit.setEnabled(ps in ('shadowsocks', 'hxsocks2')) def activateProxy(self): index = self.ui.proxyListView.currentIndex().row() piority = self.PL_table_model.mylist[index][2] if 0 <= piority <= 100: return self.on_proxy_select() self.ui.priorityEdit.setText(str(99)) self.addProxy() def disableProxy(self): self.on_proxy_select() self.ui.priorityEdit.setText(str(-1)) self.addProxy() def delProxy(self): index = self.ui.proxyListView.currentIndex().row() name = self.PL_table_model.mylist[index][0] # prompt confirm msgbox = QMessageBox() msgbox.setWindowTitle('FWLite') msgbox.setIcon(QMessageBox.Warning) msgbox.setText(_tr("MainWindow", 'Warning')) msgbox.setInformativeText( _tr("MainWindow", 'proxy_delete_info') % name) msgbox.addButton(_tr("MainWindow", 'Delete'), QMessageBox.AcceptRole) msgbox.addButton(_tr("MainWindow", 'Disable'), QMessageBox.DestructiveRole) cancel = msgbox.addButton(_tr("MainWindow", 'Cancel'), QMessageBox.RejectRole) msgbox.setDefaultButton(cancel) msgbox.setEscapeButton(cancel) reply = msgbox.exec() if reply == QMessageBox.AcceptRole: # delete proxy try: name = base64.urlsafe_b64encode(name.encode()).decode() req = Request('http://127.0.0.1:%d/api/proxy/%s' % (self.port, name), headers=self.api_auth, method='DELETE') urlopen(req, timeout=1).read() except Exception as e: print(repr(e)) elif reply == 1: # disable proxy self.disableProxy() else: return def on_proxy_select(self): button = QApplication.mouseButtons() index = self.ui.proxyListView.currentIndex().row() name = self.PL_table_model.mylist[index][0] piority = self.PL_table_model.mylist[index][2] self.load_proxy_by_name(name) self.ui.priorityEdit.setText(str(piority)) if button == Qt.RightButton: proxy = self.get_proxy_by_name(name) QApplication.instance().clipboard().setText(proxy) self.statusBar().showMessage('proxy copied to clipboard', 3000) def get_proxy_by_name(self, name): _name = base64.urlsafe_b64encode(name.encode()).decode() try: req = Request('http://127.0.0.1:%d/api/proxy/%s' % (self.port, _name), headers=self.api_auth) proxy = urlopen(req, timeout=1).read().decode() return proxy except Exception: return def load_proxy_by_name(self, name): self.ui.nameEdit.setText(name) proxy = self.get_proxy_by_name(name) self.set_ui_by_proxy_uri(proxy) def proxy_hostname_changed(self): hostname = self.ui.hostnameEdit.text() if '//' in hostname and len(hostname) > 20: try: self.set_ui_by_proxy_uri(hostname) finally: pass def set_ui_by_proxy_uri(self, proxy): # clear self.ui.encryptionBox.setCurrentIndex(0) self.ui.pskEdit.setText('') self.ui.usernameEdit.setText('') self.ui.passwordEdit.setText('') if '|' in proxy: proxy_list = proxy.split('|') proxy = proxy_list[0] via = '|'.join(proxy_list[1:]) else: via = '' parse = urllib.parse.urlparse(proxy) query = urllib.parse.parse_qs(parse.query) if parse.scheme == 'ss': self.ui.protocolBox.setCurrentIndex( SUPPORTED_PROTOCOL.index('shadowsocks')) method = parse.username password = parse.password if not password: method, password = base64.b64decode(method).decode().split( ':', 1) method_index = self.method_list.index(method) self.ui.encryptionBox.setCurrentIndex(method_index) self.ui.pskEdit.setText(password) elif parse.scheme == 'hxs2': self.ui.protocolBox.setCurrentIndex( SUPPORTED_PROTOCOL.index('hxsocks2')) method = query.get('method', ['aes-128-cfb'])[0].lower() method_index = self.method_list.index(method) self.ui.encryptionBox.setCurrentIndex(method_index) psk = query.get('PSK', [''])[0] self.ui.pskEdit.setText(psk) self.ui.usernameEdit.setText(parse.username) self.ui.passwordEdit.setText(parse.password) else: # socks5 and http self.ui.protocolBox.setCurrentIndex( SUPPORTED_PROTOCOL.index(parse.scheme) if parse.scheme else 2) self.ui.usernameEdit.setText(parse.username) self.ui.passwordEdit.setText(parse.password) self.ui.hostnameEdit.setText(parse.hostname) self.ui.portEdit.setText(str(parse.port)) self.ui.viaEdit.setText(via) # plugin plugin = query.get('plugin', [ None, ])[0] plugin_info = plugin.split(';') if plugin else None try: self.ui.pluginBox.setCurrentIndex( SUPPORTED_PLUGIN.index(plugin_info[0])) except Exception: self.ui.pluginBox.setCurrentIndex(0) if plugin_info: self.ui.plugin_optEdit.setText(';'.join(plugin_info[1:])) else: self.ui.plugin_optEdit.clear() def gfwlistToggle(self): try: req = Request('http://127.0.0.1:%d/api/gfwlist' % self.port, json.dumps( self.ui.gfwlistToggle.isChecked()).encode(), headers=self.api_auth) urlopen(req, timeout=1).read() except Exception as e: print(repr(e)) def adblockToggle(self): try: req = Request('http://127.0.0.1:%d/api/adblock' % self.port, json.dumps( self.ui.adblockToggle.isChecked()).encode(), headers=self.api_auth) urlopen(req, timeout=1).read() except Exception as e: print(repr(e)) def sysProxyToggle(self): sysproxy = self.ui.sys_proxy_toggle.isChecked() self.load_conf(self.path_to_conf) self.ieproxy = sysproxy self.conf.set('FWLite', 'ieproxy', '1' if sysproxy else '0') with open(self.path_to_conf, 'w') as f: self.conf.write(f) def startup_toggle(self): try: startup = self.ui.startup_toggle.isChecked() from .startup import set_startup set_startup(startup) except Exception as err: self.statusBar().showMessage(repr(err), 5000) def openlocal(self): self.openfile(self.path_to_local) def openconf(self): self.openfile(self.path_to_conf) def openfile(self, path): if sys.platform.startswith('win'): cmd = 'start' elif sys.platform.startswith('linux'): cmd = 'xdg-open' elif sys.platform.startswith('darwin'): cmd = 'open' else: return self.statusBar().showMessage('OS not recognised', 3000) subprocess.Popen('%s %s' % (cmd, path), shell=True) self.tray.showMessage_(_tr("MainWindow", "reload_notice")) def refresh_RR(self): try: req = Request('http://127.0.0.1:%d/api/redirector' % self.port, headers=self.api_auth) data = json.loads(urlopen(req, timeout=1).read().decode()) lst = [] self.ui.RedirectorRulesLayout.removeItem(self.spacer_RR) for redir_rule in data: rule, _, dest = redir_rule.partition(' ') if self.redir_rule_list: w = self.redir_rule_list.pop(0) w.updaterule(rule, dest) w.setVisible(True) else: w = RedirRule(rule, dest, self) self.ui.RedirectorRulesLayout.addWidget(w) lst.append(w) for w in self.redir_rule_list: w.setVisible(False) self.ui.RedirectorRulesLayout.addItem(self.spacer_RR) self.redir_rule_list = lst except Exception as e: print(repr(e)) def addRedirRule(self): rule = self.ui.RuleEdit.text() dest = self.ui.DestEdit.text() data = json.dumps((rule, dest)).encode() try: req = Request('http://127.0.0.1:%d/api/redirector' % self.port, data, headers=self.api_auth) urlopen(req, timeout=1) except Exception: self.statusBar().showMessage( 'add redirrule %s %s failed!' % (rule, dest), 3000) else: self.ui.RuleEdit.clear() self.ui.DestEdit.clear() def refresh_LR(self): # uri = 'http://127.0.0.1:%d/api/localrule' % self.port # http_request('GET', uri, cb=self._refresh_LR) try: req = Request('http://127.0.0.1:%d/api/localrule' % self.port, headers=self.api_auth) data = json.loads(urlopen(req, timeout=1).read().decode()) lst = [] self.ui.LocalRulesLayout.removeItem(self.spacer_LR) for rule, exp in data: if self.local_rule_list: w = self.local_rule_list.pop(0) w.updaterule(rule, exp) w.setVisible(True) else: w = LocalRule(rule, exp, self) self.ui.LocalRulesLayout.addWidget(w) lst.append(w) for w in self.local_rule_list: w.setVisible(False) self.ui.LocalRulesLayout.addItem(self.spacer_LR) self.local_rule_list = lst except Exception as e: print(repr(e)) def addLocalRule(self): exp = int(self.ui.ExpireEdit.text()) if self.ui.ExpireEdit.text( ).isdigit() and int(self.ui.ExpireEdit.text()) > 0 else None rule = self.ui.LocalRuleEdit.text() data = json.dumps((rule, exp)).encode() try: req = Request('http://127.0.0.1:%d/api/localrule' % self.port, data, headers=self.api_auth) urlopen(req, timeout=1).read() except Exception as e: print(repr(e)) else: self.ui.LocalRuleEdit.clear() self.ui.ExpireEdit.clear() def isgfwedTest(self): uri = self.ui.uriEdit.text() try: req = Request('http://127.0.0.1:%d/api/isgfwed' % self.port, uri.encode('utf8'), headers=self.api_auth) result = urlopen(req, timeout=1).read() self.statusBar().showMessage(result.decode('utf8'), 3000) except Exception as e: self.statusBar().showMessage(repr(e), 3000) def killProcess(self): self.runner.readyReadStandardError.connect(lambda: None) self.runner.readyReadStandardOutput.connect(lambda: None) if self.runner.state() == QProcess.Running: try: req = Request('http://127.0.0.1:%d/api/exit' % self.port, headers=self.api_auth) urlopen(req, timeout=2).read() except Exception as e: print(repr(e)) self.runner.kill() self.runner.waitForFinished(100) def createProcess(self): self.killProcess() self.load_conf(self.path_to_conf) if sys.platform.startswith('win'): # find python pdir = os.path.dirname(sys.executable) python = os.path.join(pdir, 'python.exe') else: python = sys.executable cmd = '"%s" -B -m fwlite_cli -c %s -gui' % (python, self.path_to_conf) self.runner.start(cmd) self.runner.readyReadStandardError.connect(self.newStderrInfo) self.runner.readyReadStandardOutput.connect(self.newStdoutInfo) def newStderrInfo(self): freload = False data = bytes(self.runner.readAllStandardError()) encoding = chardet.detect(data)['encoding'].lower() if chardet.detect( data)['encoding'] else 'ascii' data = data.decode(encoding) lines = data.strip().splitlines() for line in copy.copy(lines): if 'Update Completed' in line: freload = True if "error: can't start new thread" in line: freload = True elif 'dnslib_resolve_over_' in line: lines.remove(line) elif 'extend_iplist start' in line: lines.remove(line) elif 'host to iplist' in line: lines.remove(line) elif '<DNS Question:' in line: lines.remove(line) self.consoleText.extend(lines) if self.isVisible(): self.ui.console.setPlainText(u'\n'.join(self.consoleText)) self.ui.console.moveCursor(QtGui.QTextCursor.End) if freload: self.reload(clear=False) def newStdoutInfo(self): data = bytes(self.runner.readAllStandardOutput()) if not data: return data = data.decode() data_list = data.splitlines(keepends=False) for line in data_list: if line.startswith('Fwlite port: '): port = int(line[13:]) if port != self.port: self.port = port if sys.platform.startswith('win') and self.ieproxy: setIEproxy(1, u'127.0.0.1:%d' % self.port) elif line == 'all': for operation in [ self.refresh_LR, self.refresh_RR, self.refresh_proxyList, self.refresh_forwardList, self.refresh_Settings ]: if operation not in self.refresh_op: self.refresh_op.append(operation) elif line == 'local': if self.refresh_LR not in self.refresh_op: self.refresh_op.append(self.refresh_LR) elif line == 'redir': if self.refresh_RR not in self.refresh_op: self.refresh_op.append(self.refresh_RR) elif line == 'proxy': if self.refresh_proxyList not in self.refresh_op: self.refresh_op.append(self.refresh_proxyList) elif line == 'forward': if self.refresh_forwardList not in self.refresh_op: self.refresh_op.append(self.refresh_forwardList) elif line == 'settings': if self.refresh_Settings not in self.refresh_op: self.refresh_op.append(self.refresh_Settings) if self.isVisible(): for operation in self.refresh_op: operation() self.refresh_op.remove(operation) def showToggle(self): if self.isVisible(): self.hide() else: for operation in self.refresh_op: operation() self.refresh_op.remove(operation) self.ui.console.setPlainText(u'\n'.join(self.consoleText)) self.ui.console.moveCursor(QtGui.QTextCursor.End) self.ui.tabWidget.setCurrentIndex(0) self.show() if self.isMinimized(): self.showNormal() self.activateWindow() def openSetting(self): self.ui.tabWidget.setCurrentIndex(5) self.show() if self.isMinimized(): self.showNormal() self.activateWindow() def closeEvent(self, event): # hide mainwindow when close if self.isVisible(): self.hide() event.ignore() def reload(self, clear=True): if clear: self.ui.console.clear() self.consoleText.clear() self.createProcess()
def __init__(self, path_to_conf, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) icon = QIcon( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.png')) self.setWindowIcon(icon) self.port = 0 self.path_to_conf = path_to_conf self.load_conf(path_to_conf) self.tray = SystemTrayIcon(icon, self) self.tray.show() self.consoleText = deque(maxlen=300) self.runner = QProcess(self) self.refresh_op = [] # log self.ui.console.setWordWrapMode(3) # local rules self.ui.AddLocalRuleButton.clicked.connect(self.addLocalRule) self.ui.isgfwedTestButton.clicked.connect(self.isgfwedTest) self.spacer_LR = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.ui.LocalRulesLayout.addItem(self.spacer_LR) self.local_rule_list = [] # redir rules self.ui.AddRedirectorRuleButton.clicked.connect(self.addRedirRule) self.spacer_RR = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.ui.RedirectorRulesLayout.addItem(self.spacer_RR) self.redir_rule_list = [] # proxyList self.ui.proxyAddButton.clicked.connect(self.addProxy) self.ui.proxyRemoveButton.clicked.connect(self.delProxy) self.ui.protocolBox.currentIndexChanged.connect(self.protocolChanged) self.ui.proxyDisableButton.clicked.connect(self.disableProxy) self.ui.proxyActivateButton.clicked.connect(self.activateProxy) self.ui.exclusiveProxyAddButton.clicked.connect(self.exclusiveProxyAdd) self.ui.hostnameEdit.textChanged.connect(self.proxy_hostname_changed) header = [ _tr("MainWindow", "name"), _tr("MainWindow", "address"), _tr("MainWindow", "priority"), _tr("MainWindow", "resp"), ] data = [] self.PL_table_model = MyTableModel(self, data, header) self.ui.proxyListView.setModel(self.PL_table_model) self.ui.proxyListView.pressed.connect(self.on_proxy_select) import hxcrypto self.method_list = [''] self.method_list.extend( sorted(sorted(hxcrypto.method_supported.keys()), key=lambda x: hxcrypto.is_aead(x))) self.ui.encryptionBox.addItems(self.method_list) self.ui.protocolBox.addItems(SUPPORTED_PROTOCOL) # port forward self.ui.PFAddButton.clicked.connect(self.addForward) self.ui.PFRemoveButton.clicked.connect(self.delForward) header = [ _tr("MainWindow", "target"), _tr("MainWindow", "proxy"), _tr("MainWindow", "port"), ] data = [] self.PF_table_model = MyTableModel(self, data, header) self.ui.PFView.setModel(self.PF_table_model) # settings self.ui.gfwlistToggle.stateChanged.connect(self.gfwlistToggle) self.ui.adblockToggle.stateChanged.connect(self.adblockToggle) self.ui.editConfButton.clicked.connect(self.openconf) self.ui.editLocalButton.clicked.connect(self.openlocal) self.ui.sys_proxy_toggle.setCheckState( QtCore.Qt.Checked if self.ieproxy else QtCore.Qt.Unchecked) self.ui.sys_proxy_toggle.stateChanged.connect(self.sysProxyToggle) self.ui.startup_toggle.stateChanged.connect(self.startup_toggle) if not sys.platform.startswith('win'): self.ui.sys_proxy_toggle.hide() self.ui.startup_toggle.hide() else: from .startup import startup_status self.ui.startup_toggle.setCheckState( QtCore.Qt.Checked if startup_status() else QtCore.Qt.Unchecked) self.createProcess()
class ESPFirmwareFlasherWidget(QWidget): """ Used for configuring how to interact with the ESP: * Override MicroPython. """ def setup(self, mode, device_list): widget_layout = QVBoxLayout() self.setLayout(widget_layout) # Instructions grp_instructions = QGroupBox( _("How to flash MicroPython to your device")) grp_instructions_vbox = QVBoxLayout() grp_instructions.setLayout(grp_instructions_vbox) # Note: we have to specify the link color here, to something # that's suitable for both day/night/contrast themes, as the # link color is not configurable in the Qt Stylesheets instructions = _( " 1. Determine the type of device (ESP8266 or ESP32)<br />" " 2. Download firmware from the " '<a href="https://micropython.org/download" ' 'style="color:#039be5;">' "https://micropython.org/download</a><br/>" " 3. Connect your device<br/>" " 4. Load the .bin file below using the 'Browse' button<br/>" " 5. Press 'Erase & write firmware'" # "<br /><br />Check the current MicroPython version using the " # "following commands:<br />" # ">>> import sys<br />" # ">>> sys.implementation" ) label = QLabel(instructions) label.setTextFormat(Qt.RichText) label.setTextInteractionFlags(Qt.TextBrowserInteraction) label.setOpenExternalLinks(True) label.setWordWrap(True) grp_instructions_vbox.addWidget(label) widget_layout.addWidget(grp_instructions) # Device type, firmware path, flash button device_selector_label = QLabel("Device:") self.device_selector = DeviceSelector(show_label=True, icon_first=True) self.device_selector.set_device_list(device_list) device_type_label = QLabel("Choose device type:") self.device_type = QComboBox(self) self.device_type.addItem("ESP8266") self.device_type.addItem("ESP32") firmware_label = QLabel("Firmware (.bin):") self.txtFolder = QLineEdit() self.btnFolder = QPushButton(_("Browse")) self.btnExec = QPushButton(_("Erase && write firmware")) self.btnExec.setEnabled(False) form_set = QGridLayout() form_set.addWidget(device_selector_label, 0, 0) form_set.addWidget(self.device_selector, 0, 1, 1, 3) form_set.addWidget(device_type_label, 1, 0) form_set.addWidget(self.device_type, 1, 1) form_set.addWidget(firmware_label, 2, 0) form_set.addWidget(self.txtFolder, 2, 1) form_set.addWidget(self.btnFolder, 2, 2) form_set.addWidget(self.btnExec, 2, 3) widget_layout.addLayout(form_set) # Output area self.log_text_area = QPlainTextEdit() self.log_text_area.setReadOnly(True) form_set = QHBoxLayout() form_set.addWidget(self.log_text_area) widget_layout.addLayout(form_set) # Connect events self.txtFolder.textChanged.connect(self.firmware_path_changed) self.btnFolder.clicked.connect(self.show_folder_dialog) self.btnExec.clicked.connect(self.update_firmware) self.device_selector.device_changed.connect(self.toggle_exec_button) self.mode = mode def esptool_is_installed(self): """ Is the 'esptool' package installed? """ baseline, user_packages = venv.installed_packages() return "esptool" in user_packages def show_folder_dialog(self): # open dialog and set to foldername filename = QFileDialog.getOpenFileName( self, "Select MicroPython firmware (.bin)", os.path.expanduser("."), "Firmware (*.bin)", ) if filename: filename = filename[0].replace("/", os.sep) self.txtFolder.setText(filename) def update_firmware(self): baudrate = 115200 if self.mode.repl: self.mode.toggle_repl(None) if self.mode.plotter: self.mode.toggle_plotter(None) if self.mode.fs is not None: self.mode.toggle_files(None) device = self.device_selector.selected_device() if device is None: return esptool = "-mesptool" erase_command = '"{}" "{}" --port {} erase_flash'.format( venv.interpreter, esptool, device.port) if self.device_type.currentText() == "ESP32": write_command = ('"{}" "{}" --chip esp32 --port {} --baud {} ' 'write_flash -z 0x1000 "{}"').format( venv.interpreter, esptool, device.port, baudrate, self.txtFolder.text(), ) else: write_command = ('"{}" "{}" --chip esp8266 --port {} --baud {} ' 'write_flash --flash_size=detect 0 "{}"').format( venv.interpreter, esptool, device.port, baudrate, self.txtFolder.text(), ) self.commands = [erase_command, write_command] self.run_esptool() def run_esptool(self): self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.readyReadStandardError.connect(self.read_process) self.process.readyReadStandardOutput.connect(self.read_process) self.process.finished.connect(self.esptool_finished) self.process.error.connect(self.esptool_error) command = self.commands.pop(0) self.log_text_area.appendPlainText(command + "\n") self.process.start(command) def esptool_error(self, error_num): self.log_text_area.appendPlainText( "Error occurred: Error {}\n".format(error_num)) self.process = None def esptool_finished(self, exitCode, exitStatus): """ Called when the subprocess that executes 'esptool.py is finished. """ # Exit if a command fails if exitCode != 0 or exitStatus == QProcess.CrashExit: self.log_text_area.appendPlainText("Error on flashing. Aborting.") return if self.commands: self.process = None self.run_esptool() def read_process(self): """ Read data from the child process and append it to the text area. Try to keep reading until there's no more data from the process. """ msg = "" data = self.process.readAll() if data: try: msg = data.data().decode("utf-8") self.append_data(msg) except UnicodeDecodeError: pass QTimer.singleShot(2, self.read_process) def append_data(self, msg): """ Add data to the end of the text area. """ cursor = self.log_text_area.textCursor() cursor.movePosition(QTextCursor.End) cursor.insertText(msg) cursor.movePosition(QTextCursor.End) self.log_text_area.setTextCursor(cursor) def firmware_path_changed(self): self.toggle_exec_button() def toggle_exec_button(self): if (len(self.txtFolder.text()) > 0 and self.device_selector.selected_device() is not None): self.btnExec.setEnabled(True) else: self.btnExec.setEnabled(False)
def __init__(self): super().__init__() self.title = 'audiosplitter' self.initUI() self.scan_mp3() self.process = QProcess(self)
class Process(QObject): """Abstraction over a running test subprocess process. Reads the log from its stdout and parses it. Attributes: _invalid: A list of lines which could not be parsed. _data: A list of parsed lines. proc: The QProcess for the underlying process. Signals: ready: Emitted when the server finished starting up. new_data: Emitted when a new line was parsed. """ ready = pyqtSignal() new_data = pyqtSignal(object) KEYS = ['data'] def __init__(self, parent=None): super().__init__(parent) self.captured_log = [] self._invalid = [] self._data = [] self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardError) def _log(self, line): """Add the given line to the captured log output.""" # pylint: disable=no-member if pytest.config.getoption('--capture') == 'no': print(line) self.captured_log.append(line) def _parse_line(self, line): """Parse the given line from the log. Return: A self.ParseResult member. """ raise NotImplementedError def _executable_args(self): """Get the executable and arguments to pass to it as a tuple.""" raise NotImplementedError def _get_data(self): """Get the parsed data for this test. Also waits for 0.5s to make sure any new data is received. Subprocesses are expected to alias this to a public method with a better name. """ self.proc.waitForReadyRead(500) self.read_log() return self._data def _wait_signal(self, signal, timeout=5000, raising=None): """Wait for a signal to be emitted. Should be used in a contextmanager. """ blocker = pytestqt.plugin.SignalBlocker( timeout=timeout, raising=raising) blocker.connect(signal) return blocker @pyqtSlot() def read_log(self): """Read the log from the process' stdout.""" if not hasattr(self, 'proc'): # I have no idea how this happens, but it does... return while self.proc.canReadLine(): line = self.proc.readLine() line = bytes(line).decode('utf-8', errors='ignore').rstrip('\r\n') try: parsed = self._parse_line(line) except InvalidLine: self._invalid.append(line) self._log("INVALID: {}".format(line)) continue if parsed is None: if self._invalid: self._log("IGNORED: {}".format(line)) else: self._data.append(parsed) self.new_data.emit(parsed) def start(self): """Start the process and wait until it started.""" with self._wait_signal(self.ready, timeout=60000): self._start() def _start(self): """Actually start the process.""" executable, args = self._executable_args() self.proc.readyRead.connect(self.read_log) self.proc.start(executable, args) ok = self.proc.waitForStarted() assert ok assert self.is_running() def before_test(self): """Restart process before a test if it exited before.""" self._invalid = [] if not self.is_running(): self.start() def after_test(self): """Clean up data after each test. Also checks self._invalid so the test counts as failed if there were unexpected output lines earlier. """ self.captured_log = [] if self._invalid: # Wait for a bit so the full error has a chance to arrive time.sleep(1) # Exit the process to make sure we're in a defined state again self.terminate() self.clear_data() raise InvalidLine(self._invalid) self.clear_data() if not self.is_running(): raise ProcessExited def clear_data(self): """Clear the collected data.""" self._data.clear() def terminate(self): """Clean up and shut down the process.""" self.proc.terminate() self.proc.waitForFinished() def is_running(self): """Check if the process is currently running.""" return self.proc.state() == QProcess.Running def _match_data(self, value, expected): """Helper for wait_for to match a given value. The behavior of this method is slightly different depending on the types of the filtered values: - If expected is None, the filter always matches. - If the value is a string or bytes object and the expected value is too, the pattern is treated as a glob pattern (with only * active). - If the value is a string or bytes object and the expected value is a compiled regex, it is used for matching. - If the value is any other type, == is used. Return: A bool """ regex_type = type(re.compile('')) if expected is None: return True elif isinstance(expected, regex_type): return expected.match(value) elif isinstance(value, (bytes, str)): return utils.pattern_match(pattern=expected, value=value) else: return value == expected def _wait_for_existing(self, override_waited_for, **kwargs): """Check if there are any line in the history for wait_for. Return: either the found line or None. """ for line in self._data: matches = [] for key, expected in kwargs.items(): value = getattr(line, key) matches.append(self._match_data(value, expected)) if all(matches) and (not line.waited_for or override_waited_for): # If we waited for this line, chances are we don't mean the # same thing the next time we use wait_for and it matches # this line again. line.waited_for = True return line return None def _wait_for_match(self, spy, kwargs): """Try matching the kwargs with the given QSignalSpy.""" for args in spy: assert len(args) == 1 line = args[0] matches = [] for key, expected in kwargs.items(): value = getattr(line, key) matches.append(self._match_data(value, expected)) if all(matches): # If we waited for this line, chances are we don't mean the # same thing the next time we use wait_for and it matches # this line again. line.waited_for = True return line return None def _maybe_skip(self): """Can be overridden by subclasses to skip on certain log lines. We can't run pytest.skip directly while parsing the log, as that would lead to a pytest.skip.Exception error in a virtual Qt method, which means pytest-qt fails the test. Instead, we check for skip messages periodically in QuteProc._maybe_skip, and call _maybe_skip after every parsed message in wait_for (where it's most likely that new messages arrive). """ pass def wait_for(self, timeout=None, *, override_waited_for=False, do_skip=False, **kwargs): """Wait until a given value is found in the data. Keyword arguments to this function get interpreted as attributes of the searched data. Every given argument is treated as a pattern which the attribute has to match against. Args: timeout: How long to wait for the message. override_waited_for: If set, gets triggered by previous messages again. do_skip: If set, call pytest.skip on a timeout. Return: The matched line. """ __tracebackhide__ = True if timeout is None: if do_skip: timeout = 2000 elif 'CI' in os.environ: timeout = 15000 else: timeout = 5000 if not kwargs: raise TypeError("No keyword arguments given!") for key in kwargs: assert key in self.KEYS # Search existing messages existing = self._wait_for_existing(override_waited_for, **kwargs) if existing is not None: return existing # If there is none, wait for the message spy = QSignalSpy(self.new_data) elapsed_timer = QElapsedTimer() elapsed_timer.start() while True: # Skip if there are pending messages causing a skip self._maybe_skip() got_signal = spy.wait(timeout) if not got_signal or elapsed_timer.hasExpired(timeout): msg = "Timed out after {}ms waiting for {!r}.".format( timeout, kwargs) if do_skip: pytest.skip(msg) else: raise WaitForTimeout(msg) match = self._wait_for_match(spy, kwargs) if match is not None: return match def ensure_not_logged(self, delay=500, **kwargs): """Make sure the data matching the given arguments is not logged. If nothing is found in the log, we wait for delay ms to make sure nothing arrives. """ __tracebackhide__ = True try: line = self.wait_for(timeout=delay, override_waited_for=True, **kwargs) except WaitForTimeout: return else: raise BlacklistedMessageError(line)
class GUIProcess(QObject): """An external process which shows notifications in the GUI. Args: cmd: The command which was started. args: A list of arguments which gets passed. _started: Whether the underlying process is started. _proc: The underlying QProcess. _win_id: The window ID this process is used in. _what: What kind of thing is spawned (process/editor/userscript/...). Used in messages. _verbose: Whether to show more messages. Signals: error/finished/started signals proxied from QProcess. """ error = pyqtSignal(QProcess.ProcessError) finished = pyqtSignal(int, QProcess.ExitStatus) started = pyqtSignal() def __init__(self, win_id, what, *, verbose=False, additional_env=None, parent=None): super().__init__(parent) self._win_id = win_id self._what = what self._verbose = verbose self._started = False self.cmd = None self.args = None self._proc = QProcess(self) self._proc.error.connect(self.on_error) self._proc.error.connect(self.error) self._proc.finished.connect(self.on_finished) self._proc.finished.connect(self.finished) self._proc.started.connect(self.on_started) self._proc.started.connect(self.started) if additional_env is not None: procenv = QProcessEnvironment.systemEnvironment() for k, v in additional_env.items(): procenv.insert(k, v) self._proc.setProcessEnvironment(procenv) @pyqtSlot(QProcess.ProcessError) def on_error(self, error): """Show a message if there was an error while spawning.""" msg = ERROR_STRINGS[error] message.error(self._win_id, "Error while spawning {}: {}".format(self._what, msg), immediately=True) @pyqtSlot(int, QProcess.ExitStatus) def on_finished(self, code, status): """Show a message when the process finished.""" self._started = False log.procs.debug("Process finished with code {}, status {}.".format( code, status)) if status == QProcess.CrashExit: message.error(self._win_id, "{} crashed!".format(self._what.capitalize()), immediately=True) elif status == QProcess.NormalExit and code == 0: if self._verbose: message.info( self._win_id, "{} exited successfully.".format(self._what.capitalize())) else: assert status == QProcess.NormalExit message.error( self._win_id, "{} exited with status {}.".format(self._what.capitalize(), code)) @pyqtSlot() def on_started(self): """Called when the process started successfully.""" log.procs.debug("Process started.") assert not self._started self._started = True def _pre_start(self, cmd, args): """Prepare starting of a QProcess.""" if self._started: raise ValueError("Trying to start a running QProcess!") self.cmd = cmd self.args = args if self._verbose: fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args)) message.info(self._win_id, 'Executing: ' + fake_cmdline) def start(self, cmd, args, mode=None): """Convenience wrapper around QProcess::start.""" log.procs.debug("Starting process.") self._pre_start(cmd, args) if mode is None: self._proc.start(cmd, args) else: self._proc.start(cmd, args, mode) def start_detached(self, cmd, args, cwd=None): """Convenience wrapper around QProcess::startDetached.""" log.procs.debug("Starting detached.") self._pre_start(cmd, args) ok, _pid = self._proc.startDetached(cmd, args, cwd) if ok: log.procs.debug("Process started.") self._started = True else: message.error(self._win_id, "Error while spawning {}: {}.".format( self._what, self._proc.error()), immediately=True)
class HgClient(QObject): """ Class implementing the Mercurial command server interface. """ InputFormat = ">I" OutputFormat = ">cI" OutputFormatSize = struct.calcsize(OutputFormat) ReturnFormat = ">i" Channels = (b"I", b"L", b"o", b"e", b"r", b"d") def __init__(self, repoPath, encoding, vcs, parent=None): """ Constructor @param repoPath root directory of the repository (string) @param encoding encoding to be used by the command server (string) @param vcs reference to the VCS object (Hg) @param parent reference to the parent object (QObject) """ super(HgClient, self).__init__(parent) self.__server = None self.__started = False self.__version = None self.__encoding = vcs.getEncoding() self.__cancel = False self.__commandRunning = False self.__repoPath = repoPath # generate command line and environment self.__serverArgs = vcs.initCommand("serve") self.__serverArgs.append("--cmdserver") self.__serverArgs.append("pipe") self.__serverArgs.append("--config") self.__serverArgs.append("ui.interactive=True") if repoPath: self.__serverArgs.append("--repository") self.__serverArgs.append(repoPath) if encoding: self.__encoding = encoding if "--encoding" in self.__serverArgs: # use the defined encoding via the environment index = self.__serverArgs.index("--encoding") del self.__serverArgs[index:index + 2] def startServer(self): """ Public method to start the command server. @return tuple of flag indicating a successful start (boolean) and an error message (string) in case of failure """ self.__server = QProcess() self.__server.setWorkingDirectory(self.__repoPath) # connect signals self.__server.finished.connect(self.__serverFinished) prepareProcess(self.__server, self.__encoding) self.__server.start('hg', self.__serverArgs) serverStarted = self.__server.waitForStarted(15000) if not serverStarted: return False, self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.').format('hg') self.__server.setReadChannel(QProcess.StandardOutput) ok, error = self.__readHello() self.__started = ok return ok, error def stopServer(self): """ Public method to stop the command server. """ if self.__server is not None: self.__server.closeWriteChannel() res = self.__server.waitForFinished(5000) if not res: self.__server.terminate() res = self.__server.waitForFinished(3000) if not res: self.__server.kill() self.__server.waitForFinished(3000) self.__started = False self.__server.deleteLater() self.__server = None def restartServer(self): """ Public method to restart the command server. @return tuple of flag indicating a successful start (boolean) and an error message (string) in case of failure """ self.stopServer() return self.startServer() def __readHello(self): """ Private method to read the hello message sent by the command server. @return tuple of flag indicating success (boolean) and an error message in case of failure (string) """ ch, msg = self.__readChannel() if not ch: return False, self.tr("Did not receive the 'hello' message.") elif ch != "o": return False, self.tr("Received data on unexpected channel.") msg = msg.split("\n") if not msg[0].startswith("capabilities: "): return False, self.tr( "Bad 'hello' message, expected 'capabilities: '" " but got '{0}'.").format(msg[0]) self.__capabilities = msg[0][len('capabilities: '):] if not self.__capabilities: return False, self.tr("'capabilities' message did not contain" " any capability.") self.__capabilities = set(self.__capabilities.split()) if "runcommand" not in self.__capabilities: return False, "'capabilities' did not contain 'runcommand'." if not msg[1].startswith("encoding: "): return False, self.tr("Bad 'hello' message, expected 'encoding: '" " but got '{0}'.").format(msg[1]) encoding = msg[1][len('encoding: '):] if not encoding: return False, self.tr("'encoding' message did not contain" " any encoding.") self.__encoding = encoding return True, "" def __serverFinished(self, exitCode, exitStatus): """ Private slot connected to the finished signal. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) """ self.__started = False def __readChannel(self): """ Private method to read data from the command server. @return tuple of channel designator and channel data (string, integer or string or bytes) """ if (self.__server.bytesAvailable() > 0 or self.__server.waitForReadyRead(10000)): data = bytes(self.__server.peek(HgClient.OutputFormatSize)) if not data or len(data) < HgClient.OutputFormatSize: return "", "" channel, length = struct.unpack(HgClient.OutputFormat, data) channel = channel.decode(self.__encoding) if channel in "IL": self.__server.read(HgClient.OutputFormatSize) return channel, length else: if (self.__server.bytesAvailable() < HgClient.OutputFormatSize + length): return "", "" self.__server.read(HgClient.OutputFormatSize) data = self.__server.read(length) if channel == "r": return (channel, data) else: return (channel, str(data, self.__encoding, "replace")) else: return "", "" def __writeDataBlock(self, data): """ Private slot to write some data to the command server. @param data data to be sent (string) """ if not isinstance(data, bytes): data = data.encode(self.__encoding) self.__server.write( QByteArray(struct.pack(HgClient.InputFormat, len(data)))) self.__server.write(QByteArray(data)) self.__server.waitForBytesWritten() def __runcommand(self, args, inputChannels, outputChannels): """ Private method to run a command in the server (low level). @param args list of arguments for the command (list of string) @param inputChannels dictionary of input channels. The dictionary must have the keys 'I' and 'L' and each entry must be a function receiving the number of bytes to write. @param outputChannels dictionary of output channels. The dictionary must have the keys 'o' and 'e' and each entry must be a function receiving the data. @return result code of the command, -1 if the command server wasn't started or -10, if the command was canceled (integer) @exception RuntimeError raised to indicate an unexpected command channel """ if not self.__started: return -1 self.__server.write(QByteArray(b'runcommand\n')) self.__writeDataBlock('\0'.join(args)) while True: QCoreApplication.processEvents() if self.__cancel: return -10 if self.__server is None: return -1 if self.__server is None or self.__server.bytesAvailable() == 0: QThread.msleep(50) continue channel, data = self.__readChannel() # input channels if channel in inputChannels: if channel == "L": inputData, isPassword = inputChannels[channel](data) # echo the input to the output if it was a prompt if not isPassword: outputChannels["o"](inputData) else: inputData = inputChannels[channel](data) self.__writeDataBlock(inputData) # output channels elif channel in outputChannels: outputChannels[channel](data) # result channel, command is finished elif channel == "r": return struct.unpack(HgClient.ReturnFormat, data)[0] # unexpected but required channel elif channel.isupper(): raise RuntimeError( "Unexpected but required channel '{0}'.".format(channel)) # optional channels or no channel at all else: pass def __prompt(self, size, message): """ Private method to prompt the user for some input. @param size maximum length of the requested input (integer) @param message message sent by the server (string) @return data entered by the user (string) """ from .HgClientPromptDialog import HgClientPromptDialog inputData = "" isPassword = False dlg = HgClientPromptDialog(size, message) if dlg.exec_() == QDialog.Accepted: inputData = dlg.getInput() + '\n' isPassword = dlg.isPassword() return inputData, isPassword def runcommand(self, args, prompt=None, inputData=None, output=None, error=None): """ Public method to execute a command via the command server. @param args list of arguments for the command (list of string) @keyparam prompt function to reply to prompts by the server. It receives the max number of bytes to return and the contents of the output channel received so far. @keyparam inputData function to reply to bulk data requests by the server. It receives the max number of bytes to return. @keyparam output function receiving the data from the server (string). If a prompt function is given, this parameter will be ignored. @keyparam error function receiving error messages from the server (string) @return output and errors of the command server (string). In case output and/or error functions were given, the respective return value will be an empty string. """ if not self.__started: # try to start the Mercurial command server ok, startError = self.startServer() if not ok: return "", startError self.__commandRunning = True outputChannels = {} outputBuffer = None errorBuffer = None if prompt is not None or output is None: outputBuffer = io.StringIO() outputChannels["o"] = outputBuffer.write else: outputChannels["o"] = output if error: outputChannels["e"] = error else: errorBuffer = io.StringIO() outputChannels["e"] = errorBuffer.write inputChannels = {} if prompt is not None: def func(size): reply = prompt(size, outputBuffer.getvalue()) return reply, False inputChannels["L"] = func else: def myprompt(size): if outputBuffer is None: msg = self.tr("For message see output dialog.") else: msg = outputBuffer.getvalue() reply, isPassword = self.__prompt(size, msg) return reply, isPassword inputChannels["L"] = myprompt if inputData is not None: inputChannels["I"] = inputData self.__cancel = False self.__runcommand(args, inputChannels, outputChannels) if outputBuffer: out = outputBuffer.getvalue() else: out = "" if errorBuffer: err = errorBuffer.getvalue() else: err = "" self.__commandRunning = False return out, err def cancel(self): """ Public method to cancel the running command. """ self.__cancel = True self.restartServer() def wasCanceled(self): """ Public method to check, if the last command was canceled. @return flag indicating the cancel state (boolean) """ return self.__cancel def isExecuting(self): """ Public method to check, if the server is executing a command. @return flag indicating the execution of a command (boolean) """ return self.__commandRunning
def __init__(self, aPath, parent=None): super(VideoPlayer, self).__init__(parent) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAcceptDrops(True) self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.StreamPlayback) self.mediaPlayer.mediaStatusChanged.connect(self.printMediaData) self.mediaPlayer.setVolume(80) self.videoWidget = QVideoWidget(self) self.lbl = QLineEdit('00:00:00') self.lbl.setReadOnly(True) self.lbl.setFixedWidth(70) self.lbl.setUpdatesEnabled(True) self.lbl.setStyleSheet(stylesheet(self)) self.elbl = QLineEdit('00:00:00') self.elbl.setReadOnly(True) self.elbl.setFixedWidth(70) self.elbl.setUpdatesEnabled(True) self.elbl.setStyleSheet(stylesheet(self)) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setFixedWidth(32) self.playButton.setStyleSheet("background-color: black") self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal, self) self.positionSlider.setStyleSheet(stylesheet(self)) self.positionSlider.setRange(0, 100) self.positionSlider.sliderMoved.connect(self.setPosition) self.positionSlider.sliderMoved.connect(self.handleLabel) self.positionSlider.setSingleStep(2) self.positionSlider.setPageStep(20) self.positionSlider.setAttribute(Qt.WA_TranslucentBackground, True) self.clip = QApplication.clipboard() self.process = QProcess(self) self.process.readyRead.connect(self.dataReady) # self.process.started.connect(lambda: print("grabbing YouTube URL")) self.process.finished.connect(self.playFromURL) self.myurl = "" controlLayout = QHBoxLayout() controlLayout.setContentsMargins(5, 0, 5, 0) controlLayout.addWidget(self.playButton) controlLayout.addWidget(self.lbl) controlLayout.addWidget(self.positionSlider) controlLayout.addWidget(self.elbl) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.videoWidget) layout.addLayout(controlLayout) self.setLayout(layout) self.myinfo = "©2016\nAxel Schneider\n\nMouse Wheel = Zoom\nUP = Volume Up\nDOWN = Volume Down\n" + \ "LEFT = < 1 Minute\nRIGHT = > 1 Minute\n" + \ "SHIFT+LEFT = < 10 Minutes\nSHIFT+RIGHT = > 10 Minutes" self.widescreen = True #### shortcuts #### self.shortcut = QShortcut(QKeySequence("q"), self) self.shortcut.activated.connect(self.handleQuit) self.shortcut = QShortcut(QKeySequence("u"), self) self.shortcut.activated.connect(self.playFromURL) self.shortcut = QShortcut(QKeySequence("y"), self) self.shortcut.activated.connect(self.getYTUrl) self.shortcut = QShortcut(QKeySequence("o"), self) self.shortcut.activated.connect(self.openFile) self.shortcut = QShortcut(QKeySequence(" "), self) self.shortcut.activated.connect(self.play) self.shortcut = QShortcut(QKeySequence("f"), self) self.shortcut.activated.connect(self.handleFullscreen) self.shortcut = QShortcut(QKeySequence("i"), self) self.shortcut.activated.connect(self.handleInfo) self.shortcut = QShortcut(QKeySequence("s"), self) self.shortcut.activated.connect(self.toggleSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Right), self) self.shortcut.activated.connect(self.forwardSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Left), self) self.shortcut.activated.connect(self.backSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Up), self) self.shortcut.activated.connect(self.volumeUp) self.shortcut = QShortcut(QKeySequence(Qt.Key_Down), self) self.shortcut.activated.connect(self.volumeDown) self.shortcut = QShortcut( QKeySequence(Qt.ShiftModifier + Qt.Key_Right), self) self.shortcut.activated.connect(self.forwardSlider10) self.shortcut = QShortcut(QKeySequence(Qt.ShiftModifier + Qt.Key_Left), self) self.shortcut.activated.connect(self.backSlider10) self.mediaPlayer.setVideoOutput(self.videoWidget) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.positionChanged.connect(self.handleLabel) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.error.connect(self.handleError) print("QT5 Player started") self.suspend_screensaver()
class VersaProbe(QtWidgets.QWidget, _HalWidgetBase): def __init__(self, parent=None): super(VersaProbe, self).__init__(parent) if INFO.MACHINE_IS_METRIC: self.valid = QtGui.QDoubleValidator(0.0, 999.999, 3) else: self.valid = QtGui.QDoubleValidator(0.0, 99.9999, 4) self.setMinimumSize(600, 420) # Load the widgets UI file: self.filename = os.path.join(INFO.LIB_PATH, 'widgets_ui', 'versa_probe.ui') try: self.instance = uic.loadUi(self.filename, self) except AttributeError as e: LOG.critical(e) self.process_busy = False self.input_data = [ 'adj_x', 'adj_y', 'adj_z', 'adj_angle', 'probe_diam', 'max_travel', 'latch_return_dist', 'search_vel', 'probe_vel', 'rapid_vel', 'side_edge_length', 'tool_probe_height', 'tool_block_height', 'xy_clearances', 'z_clearance' ] for i in self.input_data: self['input_' + i].setValidator(self.valid) # button connections self.inside_buttonGroup.buttonClicked.connect(self.btn_inside_clicked) self.outside_buttonGroup.buttonClicked.connect( self.btn_outside_clicked) self.skew_buttonGroup.buttonClicked.connect(self.btn_skew_clicked) self.length_buttonGroup.buttonClicked.connect(self.btn_length_clicked) # check if probe macros exist self.probe_enable = True for path in INFO.SUB_PATH_LIST: path = os.path.expanduser(path) for root, dirs, files in os.walk(path): for file in files: # just need to check for one file - proof enough if 'xminus.ngc' in file: self.probe_enable = True # don't go deeper then this folder break def _hal_init(self): def homed_on_test(): return (self.probe_enable and STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED)) STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(homed_on_test())) STATUS.connect('all-homed', lambda w: self.setEnabled(homed_on_test())) STATUS.connect('error', self.send_error) STATUS.connect('periodic', lambda w: self.check_probe()) # initialize indicated buttons w = self.QTVCP_INSTANCE_ self.pbtn_allow_auto_zero.hal_init(HAL_NAME=self.HAL_NAME_ + '-auto-z-zero') self.pbtn_allow_auto_skew.hal_init(HAL_NAME=self.HAL_NAME_ + '-auto-skew') if self.PREFS_: self.input_search_vel.setText( str( self.PREFS_.getpref("ps_searchvel", 300.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_probe_vel.setText( str( self.PREFS_.getpref("ps_probevel", 10.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_z_clearance.setText( str( self.PREFS_.getpref("ps_z_clearance", 3.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_max_travel.setText( str( self.PREFS_.getpref("ps_probe_max", 1.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_latch_return_dist.setText( str( self.PREFS_.getpref("ps_probe_latch", 0.5, float, 'VERSA_PROBE_OPTIONS'))) self.input_probe_diam.setText( str( self.PREFS_.getpref("ps_probe_diam", 2.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_xy_clearances.setText( str( self.PREFS_.getpref("ps_xy_clearance", 5.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_side_edge_length.setText( str( self.PREFS_.getpref("ps_side_edge_length", 5.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_tool_probe_height.setText( str( self.PREFS_.getpref("ps_probe_height", 20.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_tool_block_height.setText( str( self.PREFS_.getpref("ps_block_height", 20.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_adj_x.setText( str( self.PREFS_.getpref("ps_offs_x", 0.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_adj_y.setText( str( self.PREFS_.getpref("ps_offs_y", 0.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_adj_z.setText( str( self.PREFS_.getpref("ps_offs_z", 0.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_adj_angle.setText( str( self.PREFS_.getpref("ps_offs_angle", 0.0, float, 'VERSA_PROBE_OPTIONS'))) self.input_rapid_vel.setText( str( self.PREFS_.getpref("ps_probe_rapid_vel", 60.0, float, 'VERSA_PROBE_OPTIONS'))) if self.probe_enable == False: LOG.error( "No path to VersaProbe Macros Found in INI's [RS274] SUBROUTINE_PATH entry" ) STATUS.emit( 'update-machine-log', 'WARNING -VersaProbe macro files not found -Probing disabled', 'TIME') else: self.read_page_data() self.start_process() # when qtvcp closes this gets called def closing_cleanup__(self): if self.PREFS_: LOG.debug('Saving Versa probe data to preference file.') self.PREFS_.putpref("ps_searchvel", float(self.input_search_vel.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probevel", float(self.input_probe_vel.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_z_clearance", float(self.input_z_clearance.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probe_max", float(self.input_max_travel.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probe_latch", float(self.input_latch_return_dist.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probe_diam", float(self.input_probe_diam.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_xy_clearance", float(self.input_xy_clearances.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_side_edge_length", float(self.input_side_edge_length.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probe_height", float(self.input_tool_probe_height.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_block_height", float(self.input_tool_block_height.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_offs_x", float(self.input_adj_x.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_offs_y", float(self.input_adj_y.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_offs_z", float(self.input_adj_z.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_offs_angle", float(self.input_adj_angle.text()), float, 'VERSA_PROBE_OPTIONS') self.PREFS_.putpref("ps_probe_rapid_vel", float(self.input_rapid_vel.text()), float, 'VERSA_PROBE_OPTIONS') if self.probe_enable == True: self.proc.terminate() ############################################# # process control ############################################# def start_process(self): self.proc = QProcess() self.proc.setReadChannel(QProcess.StandardOutput) self.proc.started.connect(self.process_started) self.proc.readyReadStandardOutput.connect(self.read_stdout) self.proc.readyReadStandardError.connect(self.read_stderror) self.proc.finished.connect(self.process_finished) self.proc.start('python {}'.format(SUBPROGRAM)) # send our PID so subprogram can check to see if it is still running self.proc.writeData('PiD_ {}\n'.format(os.getpid())) def start_probe(self, cmd): if self.process_busy is True: LOG.error("Probing processor is busy") return result = self.read_page_data() if not result: LOG.error("Error reading page data") return # clear all previous offsets ACTION.CALL_MDI("G10 L2 P0 X0 Y0 Z0") string_to_send = cmd.encode('utf-8') + ' ' + result + '\n' # print("String to send ", string_to_send) self.proc.writeData(string_to_send) self.process_busy = True def process_started(self): LOG.info("Versa_Probe started with PID {}\n".format( self.proc.processId())) def read_stdout(self): qba = self.proc.readAllStandardOutput() line = qba.data().encode('utf-8') self.parse_input(line) self.process_busy = False def read_stderror(self): qba = self.proc.readAllStandardError() line = qba.data().encode('utf-8') self.parse_input(line) def process_finished(self): print("Versa_Probe Process signals finished") def parse_input(self, line): self.process_busy = False if "ERROR" in line: print(line) elif "DEBUG" in line: print(line) elif "INFO" in line: print(line) elif "STATUS" in line: line = line.rstrip('\n') temp = line.strip('[STATUS] ').split(',') for num, i in enumerate([ 'xm', 'xc', 'xp', 'ym', 'yc', 'yp', 'lx', 'ly', 'z', 'd', 'a' ]): self['status_' + i].setText(temp[num]) LOG.info("Probing routine completed without errors") elif "HISTORY" in line: temp = line.strip('[HISTORY]') STATUS.emit('update-machine-log', temp, 'TIME') LOG.info("Probe history updated to machine log") else: LOG.error( "Error parsing return data from sub_processor. Line={}".format( line)) def send_error(self, w, kind, text): message = '_ErroR_ {},{} \n'.format(kind, text) self.proc.writeData(message) ##################################################### # button callbacks ##################################################### def help_clicked(self): self.pop_help() def btn_outside_clicked(self, button): cmd = button.property('probe') # print("Button clicked ", cmd) self.start_probe(cmd) def btn_inside_clicked(self, button): cmd = button.property('probe') # print("Button clicked ", cmd) self.start_probe(cmd) def btn_skew_clicked(self, button): cmd = button.property('probe') # print("Button clicked ", cmd) self.start_probe(cmd) def btn_length_clicked(self, button): cmd = button.property('probe') # print("Button clicked ", cmd) self.start_probe(cmd) ###### set origin offset ###################### def pbtn_set_x_released(self): ACTION.SET_AXIS_ORIGIN('X', float(self.input_adj_x.text())) def pbtn_set_y_released(self): ACTION.SET_AXIS_ORIGIN('Y', float(self.input_adj_y.text())) def pbtn_set_z_released(self): ACTION.SET_AXIS_ORIGIN('Z', float(self.input_adj_z.text())) def pbtn_set_angle_released(self): self.status_a = "%.3f" % float(self.input_adj_angle.text()) s = "G10 L2 P0" if self.pbtn_allow_auto_zero.isChecked(): s += " X%.4f" % float(self.input_adj_x.text()) s += " Y%.4f" % float(self.input_adj_y.text()) else: a = STATUS.get_probed_position_with_offsets() s += " X%.4f" % a[0] s += " Y%.4f" % a[1] s += " R%.4f" % float(self.input_adj_angle.text()) ACTION.CALL_MDI_WAIT(s, 30) ##################################################### # Helper functions ##################################################### def read_page_data(self): arg = '' for i in [ 'adj_x', 'adj_y', 'adj_z', 'adj_angle', 'probe_diam', 'max_travel', 'latch_return_dist', 'search_vel', 'probe_vel', 'rapid_vel', 'side_edge_length', 'tool_probe_height', 'tool_block_height', 'xy_clearances', 'z_clearance' ]: arg += '{},'.format(float(self['input_' + i].text())) arg += str(self.pbtn_allow_auto_zero.isChecked()) arg += ',' arg += str(self.pbtn_allow_auto_skew.isChecked()) return arg def check_probe(self): self.led_probe_function_chk.setState( hal.get_value('motion.probe-input')) def pop_help(self): d = QtWidgets.QDialog(self) d.setMinimumWidth(600) l = QtWidgets.QVBoxLayout() t = QtWidgets.QTextEdit() t.setReadOnly(False) l.addWidget(t) bBox = QtWidgets.QDialogButtonBox() bBox.addButton('Ok', QtWidgets.QDialogButtonBox.AcceptRole) bBox.accepted.connect(d.accept) l.addWidget(bBox) d.setLayout(l) try: file = QtCore.QFile(HELP) file.open(QtCore.QFile.ReadOnly) html = file.readAll() html = unicode(html, encoding='utf8') html = html.replace("../images/probe_icons/", "{}/probe_icons/".format(INFO.IMAGE_PATH)) t.setHtml(html) except Exception as e: t.setText('Versa Probe Help file Unavailable:\n\n{}'.format(e)) d.show() d.exec_() ######################################## # required boiler code ######################################## def __getitem__(self, item): return getattr(self, item) def __setitem__(self, item, value): return setattr(self, item, value)
def start(self, projectDir, patchCheckData): """ Public method to start the statistics process. @param projectDir directory name of the project (string) @param patchCheckData tuple of data as returned by the GitPatchFilesDialog.getData() method """ self.__patchCheckData = patchCheckData self.changesTreeWidget.clear() self.summaryEdit.clear() # find the root of the repo repodir = projectDir while not os.path.isdir(os.path.join(repodir, self.__vcs.adminDir)): repodir = os.path.dirname(repodir) if os.path.splitdrive(repodir)[1] == os.sep: return from .GitPatchFilesDialog import GitPatchFilesDialog dlg = GitPatchFilesDialog(repodir, patchCheckData) if dlg.exec_() == QDialog.Accepted: patchFilesList, stripCount, inaccurateEof, recount = dlg.getData() self.__patchCheckData = (patchFilesList, stripCount, inaccurateEof, recount) if patchFilesList: process = QProcess() process.setWorkingDirectory(repodir) # step 1: get the statistics args = self.__vcs.initCommand("apply") args.append("--numstat") if inaccurateEof: args.append("--inaccurate-eof") if recount: args.append("--recount") args.append("-p{0}".format(stripCount)) args.extend(patchFilesList) process.start('git', args) procStarted = process.waitForStarted(5000) if not procStarted: E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.' ).format('git')) return else: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: output = str(process.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace') for line in output.splitlines(): self.__createStatisticsItem(line) # step 2: get the summary args = self.__vcs.initCommand("apply") args.append("--summary") if inaccurateEof: args.append("--inaccurate-eof") if recount: args.append("--recount") args.append("-p{0}".format(stripCount)) args.extend(patchFilesList) process.start('git', args) procStarted = process.waitForStarted(5000) if not procStarted: E5MessageBox.critical( self, self.tr('Process Generation Error'), self.tr( 'The process {0} could not be started. ' 'Ensure, that it is in the search path.' ).format('git')) return else: finished = process.waitForFinished(30000) if finished and process.exitCode() == 0: output = str(process.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace') for line in output.splitlines(): self.summaryEdit.appendPlainText(line.strip())