Exemple #1
0
 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)
Exemple #2
0
 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'))
Exemple #3
0
    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)
Exemple #5
0
	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)
Exemple #7
0
 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[:]
Exemple #8
0
 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
Exemple #9
0
 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
Exemple #10
0
 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
Exemple #11
0
 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")])
Exemple #13
0
    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])
Exemple #14
0
    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*""")
Exemple #16
0
 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:])
Exemple #18
0
 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)
Exemple #19
0
 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
Exemple #20
0
 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()
Exemple #22
0
    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)
Exemple #25
0
 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 = ""
Exemple #26
0
 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()
Exemple #28
0
 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)
Exemple #29
0
 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)
Exemple #30
0
 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
Exemple #31
0
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())
Exemple #32
0
 def resetAll(self):
     self.settings.reset()
     self.close()
     proc = QProcess()
     proc.startDetached(os.path.abspath(__file__))
Exemple #33
0
 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
Exemple #35
0
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)
Exemple #36
0
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)
Exemple #38
0
    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
Exemple #39
0
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
Exemple #40
0
 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 ')
Exemple #41
0
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")
Exemple #42
0
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)
Exemple #43
0
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
Exemple #44
0
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,
        }
Exemple #46
0
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 = []
Exemple #48
0
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
Exemple #49
0
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)
Exemple #50
0
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()
        }
Exemple #51
0
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()
Exemple #52
0
    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()
Exemple #53
0
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 = _(
            "&nbsp;1. Determine the type of device (ESP8266 or ESP32)<br />"
            "&nbsp;2. Download firmware from the "
            '<a href="https://micropython.org/download" '
            'style="color:#039be5;">'
            "https://micropython.org/download</a><br/>"
            "&nbsp;3. Connect your device<br/>"
            "&nbsp;4. Load the .bin file below using the 'Browse' button<br/>"
            "&nbsp;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)
Exemple #54
0
 def __init__(self):
     super().__init__()
     self.title = 'audiosplitter'
     self.initUI()
     self.scan_mp3()
     self.process = QProcess(self)
Exemple #55
0
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)
Exemple #56
0
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)
Exemple #57
0
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
Exemple #58
0
    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()
Exemple #59
0
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())