class ThreadDNSspoofNF(QObject): DnsReq = pyqtSignal(object) def __init__(self,domains,interface,redirect,APmode=True,parent=None): super(ThreadDNSspoofNF, self).__init__(parent) self.domains = domains self.interface = interface self.redirect = redirect self.APmode = APmode self.desc = 'DNS spoof Module::NetFilter' @pyqtSlot() def readProcessOutput(self): self.data = str(self.procThreadDNS.readAllStandardOutput()) self.DnsReq.emit(self.data) def start(self): self.setIptables(self.APmode,option='A') self.procThreadDNS = QProcess(self) self.procThreadDNS.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procThreadDNS, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procThreadDNS.start('python',['core/packets/dnsspoofNF.py','-r',self.redirect, '-d',','.join(self.domains)]) def setIptables(self,APMode=True, option=str()): if APMode: system('iptables -{} INPUT -i {} -p udp --dport 53 -s {} -j ACCEPT'.format(option,self.interface,self.redirect)) system('iptables -{} INPUT -i {} -p udp --dport 53 -j DROP'.format(option,self.interface)) system('iptables -t nat -{} PREROUTING -p udp --dport 53 -j NFQUEUE'.format(option)) def stop(self): self.setIptables(self.APmode,option='D') if hasattr(self,'procThreadDNS'): self.procThreadDNS.terminate() self.procThreadDNS.waitForFinished() self.procThreadDNS.kill()
class ProcessThread(QObject): _ProcssOutput = pyqtSignal(object) def __init__(self,cmd,): QObject.__init__(self) self.cmd = cmd def getNameThread(self): return '[New Thread {} ({})]'.format(self.procThread.pid(),self.objectName()) @pyqtSlot() def readProcessOutput(self): self.data = str(self.procThread.readAllStandardOutput()) self._ProcssOutput.emit(self.data) def start(self): self.procThread = QProcess(self) self.procThread.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procThread, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procThread.start(self.cmd.keys()[0],self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procThread.pid(),self.objectName()) def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self,'procThread'): self.procThread.terminate() self.procThread.waitForFinished() self.procThread.kill()
class ThreadDNSspoofNF(QObject): DnsReq = pyqtSignal(object) def __init__(self,domains,interface,redirect,APmode=True,parent=None): super(ThreadDNSspoofNF, self).__init__(parent) self.domains = domains self.interface = interface self.redirect = redirect self.APmode = APmode self.desc = 'DNS spoof Module::NetFilter' @pyqtSlot() def readProcessOutput(self): self.data = str(self.procThreadDNS.readAllStandardOutput()) self.DnsReq.emit(self.data) def start(self): self.setIptables(self.APmode,option='A') self.procThreadDNS = QProcess(self) self.procThreadDNS.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procThreadDNS, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procThreadDNS.start('python',['Core/packets/dnsspoofNF.py','-r',self.redirect, '-d',','.join(self.domains)]) def setIptables(self,APMode=True, option=str()): if APMode: system('iptables -{} INPUT -i {} -p udp --dport 53 -s {} -j ACCEPT'.format(option,self.interface,self.redirect)) system('iptables -{} INPUT -i {} -p udp --dport 53 -j DROP'.format(option,self.interface)) system('iptables -t nat -{} PREROUTING -p udp --dport 53 -j NFQUEUE'.format(option)) def stop(self): self.setIptables(self.APmode,option='D') if hasattr(self,'procThreadDNS'): self.procThreadDNS.terminate() self.procThreadDNS.waitForFinished() self.procThreadDNS.kill()
class ThreadLogger(QObject): def __init__(self, logger_path=str): QObject.__init__(self) self.logger_path = logger_path @pyqtSlot() def readProcessOutput(self): try: self.emit( SIGNAL('Activated( QString )'), str(self.procLogger.readAllStandardOutput()).rstrip().split( ' : ')[1]) except Exception: pass def start(self): self.procLogger = QProcess(self) self.procLogger.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procLogger, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procLogger.start('tail', ['-f', self.logger_path]) def stop(self): if hasattr(self, 'procLogger'): self.procLogger.terminate() self.procLogger.waitForFinished() self.procLogger.kill()
class ProcessThread(QObject): _ProcssOutput = pyqtSignal(object) def __init__( self, cmd, ): QObject.__init__(self) self.cmd = cmd def getNameThread(self): return '[New Thread {} ({})]'.format(self.procThread.pid(), self.objectName()) @pyqtSlot() def readProcessOutput(self): self.data = str(self.procThread.readAllStandardOutput()) self._ProcssOutput.emit(self.data) def start(self): self.procThread = QProcess(self) self.procThread.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procThread, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procThread.start(self.cmd.keys()[0], self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procThread.pid(), self.objectName()) def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self, 'procThread'): self.procThread.terminate() self.procThread.waitForFinished() self.procThread.kill()
class ProcessHostapd(QObject): statusAP_connected = pyqtSignal(object) def __init__(self,cmd): QObject.__init__(self) self.cmd = cmd def getNameThread(self): return '[New Thread {} ({})]'.format(self.procHostapd.pid(),self.objectName()) @pyqtSlot() def read_OutputCommand(self): self.data = str(self.procHostapd.readAllStandardOutput()) if 'AP-STA-DISCONNECTED' in self.data.rstrip() or 'inactivity (timer DEAUTH/REMOVE)' in self.data.rstrip(): self.statusAP_connected.emit(self.data.split()[2]) def start(self): self.makeLogger() self.procHostapd = QProcess(self) self.procHostapd.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')); self.procHostapd.start(self.cmd.keys()[0],self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procHostapd.pid(),self.objectName()) def makeLogger(self): setup_logger('hostapd', './Logs/AccessPoint/requestAP.log') self.log_hostapd = logging.getLogger('hostapd') def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self,'procHostapd'): self.procHostapd.terminate() self.procHostapd.waitForFinished() self.procHostapd.kill()
class ProcessThread(QObject): def __init__(self,cmd,): QObject.__init__(self) self.cmd = cmd def getNameThread(self): return 'Starting Thread:' + self.objectName() @pyqtSlot() def readProcessOutput(self): self.data = str(self.procThread.readAllStandardOutput()) def start(self): print 'Starting Thread:' + self.objectName() self.procThread = QProcess(self) self.procThread.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procThread, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procThread.start(self.cmd.keys()[0],self.cmd[self.cmd.keys()[0]]) def stop(self): print 'Stop thread:' + self.objectName() if hasattr(self,'procThread'): self.procThread.terminate() self.procThread.waitForFinished() self.procThread.kill()
class PROCESS(QObject): queue_finished = pyqtSignal(int) prog_finished = pyqtSignal(tuple) stoped = pyqtSignal(int, int) # 1: start, 0: finished, 2: error | queue state = pyqtSignal(int, str) def __init__(self, parent=None): QObject.__init__(self, parent) self.process = QProcess() self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.finished.connect(self.emit_finished) self.process.stateChanged.connect(self.emit_state) self.process_type = 0 # 0 process, 1 runnable self.threadpool = QThreadPool() self.worker = Worker() self.worker.signals.state.connect(self.emit_state) self.queue = None def emit_state(self, state): self.state.emit(state, self.prog_name) def emit_finished(self, exitcode, exitstatus): self.prog_finished.emit((self.prog_name, exitcode, exitstatus)) def set_queue(self, queue): self.queue = queue def start_process(self): try: obj = self.queue.get(False) self.prog_name = obj.keys()[0] if callable(obj.values()[0][0]): self.process_type = 1 funct = obj.values()[0][0] args = obj.values()[0][1] self.worker.insert_function(funct, args, self.prog_name) self.threadpool.start(self.worker) else: self.process_type = 0 self.process.start(obj.values()[0][0], obj.values()[0][1]) except Queue.Empty: self.queue_finished.emit(self.queue.name) def force_finished(self): # for process (programs) self.stoped.emit(1, self.queue.name) if self.process_type == 0: self.process.terminate() if not self.process.waitForFinished(1000): self.process.kill() else: if self.threadpool.activeThreadCount(): self.threadpool.clear() self.threadpool.waitForDone() with self.queue.mutex: self.queue.queue.clear() self.stoped.emit(0, self.queue.name)
class ProcessHostapd(QObject): statusAP_connected = pyqtSignal(object) statusAPError = pyqtSignal(object) def __init__(self, cmd, session): QObject.__init__(self) self.cmd = cmd self.session = session self.errorAPDriver = ('AP-DISABLED', 'Failed to initialize interface', 'nl80211 driver initialization failed.', 'errors found in configuration file') def getNameThread(self): return '[New Thread {} ({})]'.format(self.procHostapd.pid(), self.objectName()) @pyqtSlot() def read_OutputCommand(self): self.data = str(self.procHostapd.readAllStandardOutput()) if 'AP-STA-DISCONNECTED' in self.data.rstrip( ) or 'inactivity (timer DEAUTH/REMOVE)' in self.data.rstrip(): self.statusAP_connected.emit(self.data.split()[2]) self.log_hostapd.info(self.data) for error in self.errorAPDriver: if self.data.find(error) != -1: return self.statusAPError.emit(self.data) def start(self): self.makeLogger() self.procHostapd = QProcess(self) self.procHostapd.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')) self.procHostapd.start(self.cmd.keys()[0], self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procHostapd.pid(), self.objectName()) def makeLogger(self): setup_logger('hostapd', C.LOG_HOSTAPD, self.session) self.log_hostapd = logging.getLogger('hostapd') def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self, 'procHostapd'): QObject.disconnect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')) self.procHostapd.terminate() self.procHostapd.waitForFinished() self.procHostapd.kill()
class ProcessHostapd(QObject): statusAP_connected = pyqtSignal(object) statusAPError = pyqtSignal(object) def __init__(self,cmd,session): QObject.__init__(self) self.cmd = cmd self.session = session self.errorAPDriver = ('AP-DISABLED', 'Failed to initialize interface', 'nl80211 driver initialization failed.', 'errors found in configuration file') def getNameThread(self): return '[New Thread {} ({})]'.format(self.procHostapd.pid(),self.objectName()) @pyqtSlot() def read_OutputCommand(self): self.data = str(self.procHostapd.readAllStandardOutput()) if 'AP-STA-DISCONNECTED' in self.data.rstrip() or 'inactivity (timer DEAUTH/REMOVE)' in self.data.rstrip(): self.statusAP_connected.emit(self.data.split()[2]) self.log_hostapd.info(self.data) for error in self.errorAPDriver: if self.data.find(error) != -1: return self.statusAPError.emit(self.data) def start(self): self.makeLogger() self.procHostapd = QProcess(self) self.procHostapd.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')); self.procHostapd.start(self.cmd.keys()[0],self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procHostapd.pid(),self.objectName()) def makeLogger(self): setup_logger('hostapd', './logs/AccessPoint/hostapd.log',self.session) self.log_hostapd = logging.getLogger('hostapd') def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self,'procHostapd'): QObject.disconnect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')) self.procHostapd.terminate() self.procHostapd.waitForFinished() self.procHostapd.kill()
class checkSoxEncoding(QObject) : """checkSoxEncoding est une class pour vérifier les possibilités de Sox pour l'encodage du format mp2/mp3. Si ce n'est pas le cas, retourne 0 -> adaptation de l'interface pour supprimer ces possibilités""" def __init__(self, listeformat, parent=None) : super(checkSoxEncoding, self).__init__(parent) #self.hide() self.listeFormat = listeformat self.parent = parent self.index = 0 self.checkListeFormat = [] def run(self) : self.soxP = QProcess(self.parent) self.connect(self.soxP, SIGNAL('finished(int)'), self.finProcess) self.write = 0 commande = u"sox -n -t %s \"%s\" trim 00:00:00.000 00:00:01.000" % (self.listeFormat[self.index], os.path.expanduser("~")+os.sep+u"checkSoxEssai."+self.listeFormat[self.index]) ### Debug print commande self.soxP.setProcessChannelMode(QProcess.MergedChannels) self.soxP.start(commande) def finProcess(self,statut) : # Test si la commande a été exécutée correctement print "statut de sortie : %d" % (statut) if not statut : # Si OK -> Ajout de l'extension dans la liste des formats supportés par Sox en encodage self.checkListeFormat.append((self.listeFormat[self.index],True)) os.remove(os.path.expanduser("~")+os.sep+u"checkSoxEssai."+self.listeFormat[self.index]) else : # Sinon, déclaré le format comme non utilisable et donc pas présent dans la liste des formats supportés self.checkListeFormat.append((self.listeFormat[self.index],False)) # Incrémentation du compteur pour explorer tous les formats à tester self.index += 1 ### Debug print "index = %d" % (self.index) print "longueur liste = %d" % (len(self.listeFormat)) # Si l'index est égal au nombre de format à tester, la class émet le signal de fin de vérification + liste des formats avec info sur le support if self.index == len(self.listeFormat) : ### Debug : print self.checkListeFormat self.emit(SIGNAL("checkSox"), self.checkListeFormat) else : self.run()
class ProcessHostapd(QObject): statusAP_connected = pyqtSignal(object) def __init__(self, cmd, session): QObject.__init__(self) self.cmd = cmd self.session = session def getNameThread(self): return '[New Thread {} ({})]'.format(self.procHostapd.pid(), self.objectName()) @pyqtSlot() def read_OutputCommand(self): self.data = str(self.procHostapd.readAllStandardOutput()) if 'AP-STA-DISCONNECTED' in self.data.rstrip( ) or 'inactivity (timer DEAUTH/REMOVE)' in self.data.rstrip(): self.statusAP_connected.emit(self.data.split()[2]) self.log_hostapd.info(self.data) def start(self): self.makeLogger() self.procHostapd = QProcess(self) self.procHostapd.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procHostapd, SIGNAL('readyReadStandardOutput()'), self, SLOT('read_OutputCommand()')) self.procHostapd.start(self.cmd.keys()[0], self.cmd[self.cmd.keys()[0]]) print '[New Thread {} ({})]'.format(self.procHostapd.pid(), self.objectName()) def makeLogger(self): setup_logger('hostapd', './Logs/AccessPoint/hostapd.log', self.session) self.log_hostapd = logging.getLogger('hostapd') def stop(self): print 'Thread::[{}] successfully stopped.'.format(self.objectName()) if hasattr(self, 'procHostapd'): self.procHostapd.terminate() self.procHostapd.waitForFinished() self.procHostapd.kill()
class ThreadLogger(QObject): def __init__(self,logger_path=str): QObject.__init__(self) self.logger_path = logger_path @pyqtSlot() def readProcessOutput(self): try: self.emit(SIGNAL('Activated( QString )'), str(self.procLogger.readAllStandardOutput()).rstrip().split(' : ')[1]) except Exception: pass def start(self): self.procLogger = QProcess(self) self.procLogger.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self.procLogger, SIGNAL('readyReadStandardOutput()'), self, SLOT('readProcessOutput()')) self.procLogger.start('tail',['-f',self.logger_path]) def stop(self): if hasattr(self,'procLogger'): self.procLogger.terminate() self.procLogger.waitForFinished() self.procLogger.kill()
class FrD_NukeBatchRender(QtGui.QMainWindow, form_class): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.setupUi(self) self.setWindowTitle('FrD Nuke Batch Render' + PROGRAM_VERSION) self.output_LW.setEnabled(0) # ============================ STYLE ======================= # # Progress Bar Style PB_STYLE = """ QProgressBar { border: 2px solid grey; border-radius: 5px; text-align: center; } QProgressBar::chunk { background-color: #d7801a; width: 3px; margin: 1.5px; } """ # self.PB.setStyleSheet(PB_STYLE) # QMenuBar Style MB_STYLE = """ QMenuBar::item { background: transparent; } QMenuBar::item:selected { background: background-color: rgb(49,49,49); border: 1px solid #000; } QMenuBar::item:pressed { background: #444; border: 1px solid #000; background-color: QLinearGradient( x1:0, y1:0, x2:0, y2:1, stop:1 #212121, stop:0.4 #343434/*, stop:0.2 #343434, stop:0.1 #ffaa00*/ ); margin-bottom:-1px; padding-bottom:1px; } """ self.menuFiles.setStyleSheet(MB_STYLE) # ============================ STYLE ======================= # # Enable Drah and Drop self.Queue_TW.setAcceptDrops(True) # Set up handlers for the TableWidget self.Queue_TW.dragEnterEvent = self.twDragEnterEvent self.Queue_TW.dragMoveEvent = self.twDragMoveEvent self.Queue_TW.dropEvent = self.twDropEvent # Size of Table Columns self.Queue_TW.setColumnWidth(0, 250) self.Queue_TW.setColumnWidth(1, 300) self.Queue_TW.setColumnWidth(2, 100) self.Queue_TW.setColumnWidth(3, 100) self.Queue_TW.setColumnWidth(4, 310) self.Queue_TW.setColumnWidth(5, 110) # operation ALL self.Clear_BN.clicked.connect(self.ClearQueue) self.Update_BN.clicked.connect(self.UpdateQueue) # Render self.Render_BN.clicked.connect(self.NBR_render) # ============ Drag and Drop Event) ============ # def twDragEnterEvent(self, event): if event.mimeData().hasUrls: event.accept() else: event.ignore() def twDragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() def twDropEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(QtCore.Qt.CopyAction) event.accept() links = [] for url in event.mimeData().urls(): links.append(str(url.toLocalFile())) # print links self.nukeProjDropped(links) else: event.ignore() # ============ Drag and Drop Ends ============ # # ============ Clear and Update All ============ # def NBR_askMsg(self, title, text): qmsgBox = QtGui.QMessageBox() qmsgBox.setStyleSheet('QMessageBox {background-color: #333; font-size: 12pt; font-family: Trebuchet MS}\nQLabel{color:white;}') # 585858 return QtGui.QMessageBox.question(qmsgBox, title, text, QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) # | QtGui.QMessageBox.No def ClearQueue(self): global folderQueue if self.Queue_TW.rowCount() > 0: if (self.NBR_askMsg('Clear Render Queue', 'Are you sure you want to clear the whole Render Queue?') == QtGui.QMessageBox.Yes): self.Queue_TW.setRowCount(0) folderQueue = [] QtGui.QApplication.processEvents() def UpdateQueue(self): global folderQueue for project in xrange(len(folderQueue)): pass # ========== Clear and Update All Ends ========== # def nukeProjDropped(self, arg): global folderQueue appendedList = [] for url in sorted(arg): if os.path.exists(url) and os.path.isfile(url) and url.lower().endswith('.nk'): if url in folderQueue: pass else: appendedList.append(url) folderQueue.append(url) # Add table row rowPosition = self.Queue_TW.rowCount() self.Queue_TW.insertRow(rowPosition) # get All Write Node from .nk # QProcess for nuke self.nukeProcess = QProcess(self) self.nukeProcess.setProcessChannelMode(QProcess.MergedChannels) args = self.createCmdArg(str(url)) self.nukeProcess.start(NUKE_EXE, args) # For debug: read output lines self.nukeProcess.readyRead.connect(self.NBR_debug) self.nukeProcess.waitForFinished() self.nukeProcess.close() # Add row and info to table self.NBR_createCheckBoxItemTableWidget(rowPosition, 0, os.path.splitext(os.path.basename(url))[0]) # Get the write nodes from text files (D:/Temp/nukeBatch) layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignLeft) with open("D:\\Temp\\nukeBatch\\" + os.path.basename(url) + ".txt", "r") as ins: bframe = False bCount = 0 for line in ins: if 'FrameRange:' in line: bframe = True continue if (bframe is False): item = QtGui.QCheckBox(str(line).strip()) item.setChecked(True) item.setFont(QtGui.QFont('meiryo', 9)) layout.addWidget(item) elif (bframe is True): self.NBR_createEditTextItemTableWidget(rowPosition, 2 + bCount, int(str(line).strip())) bCount += 1 cellWidget = QtGui.QWidget() cellWidget.setLayout(layout) self.Queue_TW.setCellWidget(rowPosition, 1, cellWidget) self.Queue_TW.resizeRowsToContents() self.Queue_TW.scrollToBottom() QtGui.QApplication.processEvents() def createCmdArg(self, nk): # nk = url return ["-ti", CHECKING_NK, nk] def createCmdArg2(self, nk, start, end, wnode=[]): return ["-ti", RENDER_NK, nk, wnode, start, end] def NBR_debug(self): while (self.nukeProcess.canReadLine()): print (str(self.nukeProcess.readLine())) def NBR_createCheckBoxItemTableWidget(self, row, col, text, color='#4c4c4c'): layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) item = QtGui.QCheckBox(text) item.setChecked(True) item.setFont(QtGui.QFont('meiryo', 9)) layout.addWidget(item) cellWidget = QtGui.QWidget() cellWidget.setStyleSheet('color:white; background-color:' + color) cellWidget.setLayout(layout) self.Queue_TW.setCellWidget(row, col, cellWidget) # self.Queue_TW.setItem(row, 0, QtGui.QTableWidgetItem(str(text))) def NBR_createEditTextItemTableWidget(self, row, col, text, color='#4c4c4c'): layout = QtGui.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignCenter) item = QtGui.QSpinBox() item.setMaximum(9999) item.setValue(text) item.setFont(QtGui.QFont('meiryo', 9)) layout.addWidget(item) cellWidget = QtGui.QWidget() cellWidget.setStyleSheet('color:white; background-color:' + color) cellWidget.setLayout(layout) self.Queue_TW.setCellWidget(row, col, cellWidget) def NBR_getCellData(self, row, col, key): cellDict = {} target = self.Queue_TW.cellWidget(row, col) if target: lay = target.layout() if lay: for k in xrange(lay.count()): w = lay.itemAt(k).widget() if isinstance(w, QtGui.QCheckBox): cellDict[key] = w.checkState() # 0 false, > 0 true elif isinstance(w, QtGui.QSpinBox): cellDict[key] = w.value() return cellDict def NBR_getCellNodeData(self, row, col): target = self.Queue_TW.cellWidget(row, col) cellUserInputList = [] if target: lay = target.layout() if lay: for k in xrange(lay.count()): w = lay.itemAt(k).widget() if isinstance(w, QtGui.QCheckBox): cellUserInputList.append([str(w.text()), w.checkState()]) else: cellUserInputList.append(-1) return cellUserInputList def NBR_render(self): global folderQueue for nk in xrange(len(folderQueue)): renderNode = [] # proj = os.path.splitext(os.path.basename(folderQueue[nk]))[0] projDict = self.NBR_getCellData(nk, 0, 'Project') if int(projDict['Project']) > 0: nodeArr = self.NBR_getCellNodeData(nk, 1) startDict = self.NBR_getCellData(nk, 2, 'Start') endDict = self.NBR_getCellData(nk, 3, 'End') # Write nodes loop for node in xrange(len(nodeArr)): if nodeArr[node][1] > 0: # get each node check state (0/2), selected write node (render) renderNode.append(nodeArr[node][0].split(":")[0]) # Debug print str(renderNode) # print nodeArr self.nukeProcess = QProcess(self) # QProcess for nuke print nodeArr[node][0].split(":")[0] # get each node from array self.nukeProcess.setProcessChannelMode(QProcess.MergedChannels) args = self.createCmdArg2(str(folderQueue[nk]), renderNode, startDict['Start'], endDict['End']) self.nukeProcess.start(NUKE_EXE, str(args)) self.nukeProcess.readyRead.connect(self.NBR_debug) self.nukeProcess.waitForFinished() self.nukeProcess.close()
class ExternalPythonShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = ExtPythonShellWidget def __init__(self, parent=None, fname=None, wdir=None, commands=[], interact=False, debug=False, path=[], ipython=False, arguments=None, stand_alone=True, umd_enabled=True, umd_namelist=[], umd_verbose=True): self.namespacebrowser = None # namespace browser widget! self.fname = startup.__file__ if fname is None else fname self.stand_alone = stand_alone self.umd_enabled = umd_enabled self.umd_namelist = umd_namelist self.umd_verbose = umd_verbose self.namespacebrowser_button = None self.cwd_button = None self.terminate_button = None ExternalShellBase.__init__(self, parent, wdir, history_filename='.history.py') self.nsb_timer = QTimer(self) # Namespace browser auto-refresh timer self.nsb_timer.setInterval(3000) if arguments is not None: assert isinstance(arguments, basestring) self.arguments = arguments self.ipython = ipython if self.ipython: interact = False self.shell.set_externalshell(self) self.toggle_globals_explorer(False) self.interact_action.setChecked(interact) self.debug_action.setChecked(debug) self.monitor_socket = None self.interpreter = fname is None if self.interpreter: self.terminate_button.hide() self.commands = ["import sys", "sys.path.insert(0, '')"] + commands # Additional python path list self.path = path def closeEvent(self, event): ExternalShellBase.closeEvent(self, event) self.disconnect(self.nsb_timer, SIGNAL("timeout()"), self.namespacebrowser.refresh_table) def get_toolbar_buttons(self): ExternalShellBase.get_toolbar_buttons(self) if self.namespacebrowser_button is None and self.stand_alone: self.namespacebrowser_button = create_toolbutton(self, get_icon('dictedit.png'), self.tr("Variables"), tip=self.tr("Show/hide global variables explorer"), toggled=self.toggle_globals_explorer) if self.cwd_button is None: self.cwd_button = create_toolbutton(self, get_std_icon('DirOpenIcon'), self.tr("Working directory"), tip=self.tr("Set current working directory"), triggered=self.set_current_working_directory) if self.terminate_button is None: self.terminate_button = create_toolbutton(self, get_icon('terminate.png'), self.tr("Terminate"), tip=self.tr("Attempts to terminate the process.\n" "The process may not exit as a result of " "clicking this button\n" "(it is given the chance to prompt " "the user for any unsaved files, etc).")) buttons = [self.cwd_button] if self.namespacebrowser_button is not None: buttons.append(self.namespacebrowser_button) buttons += [self.run_button, self.options_button, self.terminate_button, self.kill_button] return buttons def get_options_menu(self): self.interact_action = create_action(self, self.tr("Interact")) self.interact_action.setCheckable(True) self.debug_action = create_action(self, self.tr("Debug")) self.debug_action.setCheckable(True) self.args_action = create_action(self, self.tr("Arguments..."), triggered=self.get_arguments) return [self.interact_action, self.debug_action, self.args_action] def is_interpreter(self): """Return True if shellwidget is a Python/IPython interpreter""" return self.interpreter def set_namespacebrowser(self, namespacebrowser): """Set namespace browser *widget*""" self.namespacebrowser = namespacebrowser def get_shell_widget(self): if self.stand_alone: self.namespacebrowser = NamespaceBrowser(self) self.namespacebrowser.set_shellwidget(self) self.connect(self.namespacebrowser, SIGNAL('collapse()'), lambda: self.toggle_globals_explorer(False)) # Shell splitter self.splitter = splitter = QSplitter(Qt.Vertical, self) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.splitter_moved) splitter.addWidget(self.shell) splitter.setCollapsible(0, False) splitter.addWidget(self.namespacebrowser) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 0) splitter.setHandleWidth(5) splitter.setSizes([2, 1]) return splitter else: return self.shell def get_icon(self): return get_icon('python.png') def set_buttons_runnning_state(self, state): ExternalShellBase.set_buttons_runnning_state(self, state) self.interact_action.setEnabled(not state and not self.interpreter) self.debug_action.setEnabled(not state and not self.interpreter) self.args_action.setEnabled(not state and (not self.interpreter or self.ipython)) if state: if self.arguments: argstr = self.tr("Arguments: %1").arg(self.arguments) else: argstr = self.tr("No argument") else: argstr = self.tr("Arguments...") self.args_action.setText(argstr) self.terminate_button.setEnabled(state) if not state: self.toggle_globals_explorer(False) self.cwd_button.setEnabled(state) if self.namespacebrowser_button is not None: self.namespacebrowser_button.setEnabled(state) def create_process(self): self.shell.clear() self.process = QProcess(self) if self.ipython: self.process.setProcessChannelMode(QProcess.MergedChannels) else: self.process.setProcessChannelMode(QProcess.SeparateChannels) self.connect(self.shell, SIGNAL("wait_for_ready_read()"), lambda: self.process.waitForReadyRead(250)) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) #-------------------------Python specific------------------------------- # Python arguments p_args = ['-u'] if self.interact_action.isChecked(): p_args.append('-i') if self.debug_action.isChecked(): p_args.extend(['-m', 'pdb']) if os.name == 'nt': # When calling pdb on Windows, one has to double the backslashes # to help Python escaping these characters (otherwise, for example, # '\t' will be interpreted as a tabulation): p_args.append(self.fname.replace(os.sep, os.sep*2)) else: p_args.append(self.fname) env = self.process.systemEnvironment() # Monitor env.append('SHELL_ID=%s' % id(self)) from spyderlib.widgets.externalshell.monitor import start_server server, port = start_server() self.notification_thread = server.register(str(id(self)), self) self.connect(self.notification_thread, SIGNAL('refresh()'), self.namespacebrowser.refresh_table) env.append('SPYDER_PORT=%d' % port) # Python init commands (interpreter only) if self.commands and self.interpreter: env.append('PYTHONINITCOMMANDS=%s' % ';'.join(self.commands)) self.process.setEnvironment(env) # User Module Deleter if self.interpreter: env.append('UMD_ENABLED=%r' % self.umd_enabled) env.append('UMD_NAMELIST=%s' % ','.join(self.umd_namelist)) env.append('UMD_VERBOSE=%r' % self.umd_verbose) # IPython related configuration if self.ipython: env.append('IPYTHON=True') # Do not call msvcrt.getch in IPython.genutils.page_more: env.append('TERM=emacs') pathlist = [] # Fix encoding with custom "sitecustomize.py" scpath = osp.dirname(osp.abspath(__file__)) pathlist.append(scpath) # Adding Spyder path pathlist += self.path # Adding path list to PYTHONPATH environment variable add_pathlist_to_PYTHONPATH(env, pathlist) self.process.setEnvironment(env) #-------------------------Python specific------------------------------- if self.arguments: p_args.extend( self.arguments.split(' ') ) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), self.write_error) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.terminate_button, SIGNAL("clicked()"), self.process.terminate) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) #-------------------------Python specific------------------------------- self.process.start(sys.executable, p_args) #-------------------------Python specific------------------------------- running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, self.tr("Error"), self.tr("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) self.connect(self.nsb_timer, SIGNAL("timeout()"), self.namespacebrowser.auto_refresh) self.nsb_timer.start() return self.process #=============================================================================== # Input/Output #=============================================================================== def write_error(self): #---This is apparently necessary only on Windows (not sure though): # emptying standard output buffer before writing error output self.process.setReadChannel(QProcess.StandardOutput) if self.process.waitForReadyRead(1): self.write_output() self.shell.write_error(self.get_stderr()) QApplication.processEvents() def send_to_process(self, qstr): if not isinstance(qstr, QString): qstr = QString(qstr) if not qstr.endsWith('\n'): qstr.append('\n') self.process.write(qstr.toLocal8Bit()) self.process.waitForBytesWritten(-1) # Eventually write prompt faster (when hitting Enter continuously) # -- necessary/working on Windows only: if os.name == 'nt': self.write_error() def keyboard_interrupt(self): communicate(self.monitor_socket, "thread.interrupt_main()") #=============================================================================== # Globals explorer #=============================================================================== def toggle_globals_explorer(self, state): if self.stand_alone: self.splitter.setSizes([1, 1 if state else 0]) self.namespacebrowser_button.setChecked(state) if state: self.namespacebrowser.refresh_table() def splitter_moved(self, pos, index): self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] ) #=============================================================================== # Current working directory #=============================================================================== def set_current_working_directory(self): cwd = self.shell.get_cwd() self.emit(SIGNAL('redirect_stdio(bool)'), False) directory = QFileDialog.getExistingDirectory(self, self.tr("Select directory"), cwd) if not directory.isEmpty(): self.shell.set_cwd(unicode(directory)) self.emit(SIGNAL('redirect_stdio(bool)'), True)
class ExternalPythonShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = ExtPyQsciShell def __init__(self, parent=None, fname=None, wdir=None, commands=[], interact=False, debug=False, path=[]): ExternalShellBase.__init__(self, parent, wdir, history_filename='.history_ec.py') self.shell.set_externalshell(self) self.toggle_globals_explorer(False) self.interact_check.setChecked(interact) self.debug_check.setChecked(debug) self.monitor_socket = None self.interpreter = fname is None self.fname = startup.__file__ if fname is None else fname if self.interpreter: self.interact_check.hide() self.debug_check.hide() self.terminate_button.hide() self.commands = ["import sys", "sys.path.insert(0, '')"] + commands # Additional python path list self.path = path def get_toolbar_buttons(self): ExternalShellBase.get_toolbar_buttons(self) self.globalsexplorer_button = create_toolbutton( self, get_icon('dictedit.png'), self.tr("Variables"), tip=self.tr("Show/hide global variables explorer"), toggled=self.toggle_globals_explorer) self.terminate_button = create_toolbutton( self, get_icon('terminate.png'), self.tr("Terminate"), tip=self.tr("Attempts to terminate the process.\n" "The process may not exit as a result of clicking " "this button\n(it is given the chance to prompt " "the user for any unsaved files, etc).")) self.interact_check = QCheckBox(self.tr("Interact"), self) self.debug_check = QCheckBox(self.tr("Debug"), self) return [ self.interact_check, self.debug_check, self.globalsexplorer_button, self.run_button, self.terminate_button, self.kill_button ] def get_shell_widget(self): # Globals explorer self.globalsexplorer = GlobalsExplorer(self) self.connect(self.globalsexplorer, SIGNAL('collapse()'), lambda: self.toggle_globals_explorer(False)) # Shell splitter self.splitter = splitter = QSplitter(Qt.Vertical, self) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.splitter_moved) splitter.addWidget(self.shell) splitter.setCollapsible(0, False) splitter.addWidget(self.globalsexplorer) splitter.setStretchFactor(0, 2) splitter.setStretchFactor(1, 1) return splitter def get_icon(self): return get_icon('python.png') def set_buttons_runnning_state(self, state): ExternalShellBase.set_buttons_runnning_state(self, state) self.interact_check.setEnabled(not state) self.debug_check.setEnabled(not state) self.terminate_button.setEnabled(state) if not state: self.toggle_globals_explorer(False) self.globalsexplorer_button.setEnabled(state) def create_process(self): self.shell.clear() self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) self.connect(self.shell, SIGNAL("wait_for_ready_read()"), lambda: self.process.waitForReadyRead(250)) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) #-------------------------Python specific------------------------------- # Python arguments p_args = ['-u'] if self.interact_check.isChecked(): p_args.append('-i') if self.debug_check.isChecked(): p_args.extend(['-m', 'pdb']) p_args.append(self.fname) env = self.process.systemEnvironment() # Monitor env.append('SHELL_ID=%s' % id(self)) from spyderlib.widgets.externalshell.monitor import start_server server, port = start_server() self.notification_thread = server.register(str(id(self)), self) self.connect(self.notification_thread, SIGNAL('refresh()'), self.globalsexplorer.refresh_table) env.append('SPYDER_PORT=%d' % port) # Python init commands (interpreter only) if self.commands and self.interpreter: env.append('PYTHONINITCOMMANDS=%s' % ';'.join(self.commands)) self.process.setEnvironment(env) pathlist = [] # Fix encoding with custom "sitecustomize.py" scpath = osp.dirname(osp.abspath(__file__)) pathlist.append(scpath) # Adding Spyder path pathlist += self.path # Adding path list to PYTHONPATH environment variable pypath = "PYTHONPATH" pathstr = os.pathsep.join(pathlist) if os.environ.get(pypath) is not None: env.replaceInStrings(pypath + '=', pypath + '=' + pathstr + os.pathsep, Qt.CaseSensitive) else: env.append(pypath + '=' + pathstr) self.process.setEnvironment(env) #-------------------------Python specific------------------------------- if self.arguments: p_args.extend(self.arguments.split(' ')) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.terminate_button, SIGNAL("clicked()"), self.process.terminate) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) #-------------------------Python specific------------------------------- self.process.start(sys.executable, p_args) #-------------------------Python specific------------------------------- running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, self.tr("Error"), self.tr("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process #=============================================================================== # Input/Output #=============================================================================== def _write_error(self, text, findstr): pos = text.find(findstr) if pos != -1: self.shell.write(text[:pos]) if text.endswith(">>> "): self.shell.write_error(text[pos:-5]) self.shell.write(text[-5:], flush=True) else: self.shell.write_error(text[pos:]) return True return False def write_output(self): text = self.get_stdout() if not self._write_error(text, 'Traceback (most recent call last):') \ and not self._write_error(text, 'File "<stdin>", line 1'): self.shell.write(text) QApplication.processEvents() def send_to_process(self, qstr): if not isinstance(qstr, QString): qstr = QString(qstr) if not qstr.endsWith('\n'): qstr.append('\n') self.process.write(qstr.toLocal8Bit()) self.process.waitForBytesWritten(-1) def keyboard_interrupt(self): communicate(self.monitor_socket, "thread.interrupt_main()") #=============================================================================== # Globals explorer #=============================================================================== def toggle_globals_explorer(self, state): self.splitter.setSizes([1, 1 if state else 0]) self.globalsexplorer_button.setChecked(state) if state: self.globalsexplorer.refresh_table() def splitter_moved(self, pos, index): self.globalsexplorer_button.setChecked(self.splitter.sizes()[1])
class ExternalSystemShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = TerminalWidget def __init__(self, parent=None, wdir=None): ExternalShellBase.__init__(self, parent, wdir, history_filename='.history_ec') def get_icon(self): return get_icon('cmdprompt.png') def create_process(self): self.shell.clear() self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) # Shell arguments if os.name == 'nt': p_args = ['/Q'] else: p_args = ['-i'] if self.arguments: p_args.extend( self.arguments.split(' ') ) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) if os.name == 'nt': self.process.start('cmd.exe', p_args) else: # Using bash: self.process.start('bash', p_args) self.send_to_process("""PS1="\u@\h:\w> "\n""") running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, self.tr("Error"), self.tr("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process #=============================================================================== # Input/Output #=============================================================================== def transcode(self, bytes): if os.name == 'nt': return encoding.transcode(str(bytes.data()), 'cp850') else: return ExternalShellBase.transcode(self, bytes) def send_to_process(self, qstr): if not isinstance(qstr, QString): qstr = QString(qstr) if qstr[:-1] in ["clear", "cls", "CLS"]: self.shell.clear() self.send_to_process(QString(os.linesep)) return if not qstr.endsWith('\n'): qstr.append('\n') if os.name == 'nt': self.process.write(unicode(qstr).encode('cp850')) else: self.process.write(qstr.toLocal8Bit()) self.process.waitForBytesWritten(-1) def keyboard_interrupt(self): # This does not work on Windows: # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe) self.send_ctrl_to_process('c') # # The following code will soon be removed: # # (last attempt to send a Ctrl+C on Windows) # if os.name == 'nt': # pid = int(self.process.pid()) # import ctypes, win32api, win32con # class _PROCESS_INFORMATION(ctypes.Structure): # _fields_ = [("hProcess", ctypes.c_int), # ("hThread", ctypes.c_int), # ("dwProcessID", ctypes.c_int), # ("dwThreadID", ctypes.c_int)] # x = ctypes.cast( ctypes.c_void_p(pid), # ctypes.POINTER(_PROCESS_INFORMATION) ) # win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, # x.dwProcessID) # else: # self.send_ctrl_to_process('c')
class ExternalPythonShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = ExtPythonShellWidget def __init__(self, parent=None, fname=None, wdir=None, interact=False, debug=False, path=[], python_args='', ipykernel=False, arguments='', stand_alone=None, umd_enabled=True, umd_namelist=[], umd_verbose=True, pythonstartup=None, pythonexecutable=None, monitor_enabled=True, mpl_patch_enabled=True, mpl_backend=None, ets_backend='qt4', qt_api=None, pyqt_api=0, install_qt_inputhook=True, ignore_sip_setapi_errors=False, merge_output_channels=False, colorize_sys_stderr=False, autorefresh_timeout=3000, autorefresh_state=True, light_background=True, menu_actions=None, show_buttons_inside=True, show_elapsed_time=True): assert qt_api in (None, 'pyqt', 'pyside') self.namespacebrowser = None # namespace browser widget! self.dialog_manager = DialogManager() self.stand_alone = stand_alone # stand alone settings (None: plugin) self.pythonstartup = pythonstartup self.pythonexecutable = pythonexecutable self.monitor_enabled = monitor_enabled self.mpl_patch_enabled = mpl_patch_enabled self.mpl_backend = mpl_backend self.ets_backend = ets_backend self.qt_api = qt_api self.pyqt_api = pyqt_api self.install_qt_inputhook = install_qt_inputhook self.ignore_sip_setapi_errors = ignore_sip_setapi_errors self.merge_output_channels = merge_output_channels self.colorize_sys_stderr = colorize_sys_stderr self.umd_enabled = umd_enabled self.umd_namelist = umd_namelist self.umd_verbose = umd_verbose self.autorefresh_timeout = autorefresh_timeout self.autorefresh_state = autorefresh_state self.namespacebrowser_button = None self.cwd_button = None self.env_button = None self.syspath_button = None self.terminate_button = None self.notification_thread = None ExternalShellBase.__init__(self, parent=parent, fname=fname, wdir=wdir, history_filename='.history.py', light_background=light_background, menu_actions=menu_actions, show_buttons_inside=show_buttons_inside, show_elapsed_time=show_elapsed_time) if self.pythonexecutable is None: self.pythonexecutable = get_python_executable() self.python_args = None if python_args: assert isinstance(python_args, basestring) self.python_args = python_args assert isinstance(arguments, basestring) self.arguments = arguments self.connection_file = None self.is_ipykernel = ipykernel if self.is_ipykernel: interact = False # Running our custom startup script for IPython kernels: # (see spyderlib/widgets/externalshell/start_ipython_kernel.py) self.fname = get_module_source_path('SMlib.widgets.externalshell', 'start_ipython_kernel.py') self.shell.set_externalshell(self) self.toggle_globals_explorer(False) self.interact_action.setChecked(interact) self.debug_action.setChecked(debug) self.introspection_socket = None self.is_interpreter = fname is None if self.is_interpreter: self.terminate_button.hide() # Additional python path list self.path = path self.shell.path = path def set_introspection_socket(self, introspection_socket): self.introspection_socket = introspection_socket if self.namespacebrowser is not None: settings = self.namespacebrowser.get_view_settings() communicate(introspection_socket, 'set_remote_view_settings()', settings=[settings]) def set_autorefresh_timeout(self, interval): if self.introspection_socket is not None: try: communicate(self.introspection_socket, "set_monitor_timeout(%d)" % interval) except socket.error: pass def closeEvent(self, event): self.quit_monitor() ExternalShellBase.closeEvent(self, event) def get_toolbar_buttons(self): ExternalShellBase.get_toolbar_buttons(self) if self.namespacebrowser_button is None \ and self.stand_alone is not None: self.namespacebrowser_button = create_toolbutton( self, text=_("Variables"), icon=get_icon('dictedit.png'), tip=_("Show/hide global variables explorer"), toggled=self.toggle_globals_explorer, text_beside_icon=True) if self.terminate_button is None: self.terminate_button = create_toolbutton( self, text=_("Terminate"), icon=get_icon('terminate.png'), tip=_("""Attempts to terminate the process. The process may not exit as a result of clicking this button (it is given the chance to prompt the user for any unsaved files, etc).""")) buttons = [] if self.namespacebrowser_button is not None: buttons.append(self.namespacebrowser_button) buttons += [ self.run_button, self.options_button, self.terminate_button, self.kill_button ] return buttons def get_options_menu(self): ExternalShellBase.get_options_menu(self) self.interact_action = create_action(self, _("Interact")) self.interact_action.setCheckable(True) self.debug_action = create_action(self, _("Debug")) self.debug_action.setCheckable(True) self.args_action = create_action(self, _("Arguments..."), triggered=self.get_arguments) run_settings_menu = QMenu(_("Run settings"), self) add_actions( run_settings_menu, (self.interact_action, self.debug_action, self.args_action)) self.cwd_button = create_action( self, _("Working directory"), icon=get_std_icon('DirOpenIcon'), tip=_("Set current working directory"), triggered=self.set_current_working_directory) self.env_button = create_action(self, _("Environment variables"), icon=get_icon('environ.png'), triggered=self.show_env) self.syspath_button = create_action(self, _("Show sys.path contents"), icon=get_icon('syspath.png'), triggered=self.show_syspath) actions = [ run_settings_menu, self.show_time_action, None, self.cwd_button, self.env_button, self.syspath_button ] if self.menu_actions is not None: actions += [None] + self.menu_actions return actions def is_interpreter(self): """Return True if shellwidget is a Python interpreter""" return self.is_interpreter def get_shell_widget(self): if self.stand_alone is None: return self.shell else: self.namespacebrowser = NamespaceBrowser(self) settings = self.stand_alone self.namespacebrowser.set_shellwidget(self) self.namespacebrowser.setup(**settings) self.connect(self.namespacebrowser, SIGNAL('collapse()'), lambda: self.toggle_globals_explorer(False)) # Shell splitter self.splitter = splitter = QSplitter(Qt.Vertical, self) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.splitter_moved) splitter.addWidget(self.shell) splitter.setCollapsible(0, False) splitter.addWidget(self.namespacebrowser) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 0) splitter.setHandleWidth(5) splitter.setSizes([2, 1]) return splitter def get_icon(self): return get_icon('python.png') def set_buttons_runnning_state(self, state): ExternalShellBase.set_buttons_runnning_state(self, state) self.interact_action.setEnabled(not state and not self.is_interpreter) self.debug_action.setEnabled(not state and not self.is_interpreter) self.args_action.setEnabled(not state and not self.is_interpreter) if state: if self.arguments: argstr = _("Arguments: %s") % self.arguments else: argstr = _("No argument") else: argstr = _("Arguments...") self.args_action.setText(argstr) self.terminate_button.setVisible(not self.is_interpreter and state) if not state: self.toggle_globals_explorer(False) for btn in (self.cwd_button, self.env_button, self.syspath_button): btn.setEnabled(state and self.monitor_enabled) if self.namespacebrowser_button is not None: self.namespacebrowser_button.setEnabled(state) def set_namespacebrowser(self, namespacebrowser): """ Set namespace browser *widget* Note: this method is not used in stand alone mode """ self.namespacebrowser = namespacebrowser self.configure_namespacebrowser() def configure_namespacebrowser(self): """Connect the namespace browser to the notification thread""" if self.notification_thread is not None: self.connect(self.notification_thread, SIGNAL('refresh_namespace_browser()'), self.namespacebrowser.refresh_table) signal = self.notification_thread.sig_process_remote_view signal.connect(self.namespacebrowser.process_remote_view) def create_process(self): self.shell.clear() self.process = QProcess(self) if self.merge_output_channels: self.process.setProcessChannelMode(QProcess.MergedChannels) else: self.process.setProcessChannelMode(QProcess.SeparateChannels) self.connect(self.shell, SIGNAL("wait_for_ready_read()"), lambda: self.process.waitForReadyRead(250)) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) #-------------------------Python specific------------------------------- # Python arguments p_args = ['-u'] if DEBUG >= 3: p_args += ['-v'] p_args += get_python_args(self.fname, self.python_args, self.interact_action.isChecked(), self.debug_action.isChecked(), self.arguments) env = [unicode(_path) for _path in self.process.systemEnvironment()] if self.pythonstartup: env.append('PYTHONSTARTUP=%s' % self.pythonstartup) # Monitor if self.monitor_enabled: env.append('SPYDER_SHELL_ID=%s' % id(self)) env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout) env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state) from SMlib.widgets.externalshell import introspection introspection_server = introspection.start_introspection_server() introspection_server.register(self) notification_server = introspection.start_notification_server() self.notification_thread = notification_server.register(self) self.connect( self.notification_thread, SIGNAL('pdb(QString,int)'), lambda fname, lineno: self.emit( SIGNAL('pdb(QString,int)'), fname, lineno)) self.connect( self.notification_thread, SIGNAL('new_ipython_kernel(QString)'), lambda args: self.emit( SIGNAL('create_ipython_client(QString)'), args)) self.connect( self.notification_thread, SIGNAL('open_file(QString,int)'), lambda fname, lineno: self.emit( SIGNAL('open_file(QString,int)'), fname, lineno)) if self.namespacebrowser is not None: self.configure_namespacebrowser() env.append('SPYDER_I_PORT=%d' % introspection_server.port) env.append('SPYDER_N_PORT=%d' % notification_server.port) # External modules options env.append('ETS_TOOLKIT=%s' % self.ets_backend) env.append('MATPLOTLIB_PATCH=%r' % self.mpl_patch_enabled) if self.mpl_backend: env.append('MATPLOTLIB_BACKEND=%s' % self.mpl_backend) if self.qt_api: env.append('QT_API=%s' % self.qt_api) env.append('INSTALL_QT_INPUTHOOK=%s' % self.install_qt_inputhook) env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr) # # Socket-based alternative (see input hook in sitecustomize.py): # if self.install_qt_inputhook: # from PyQt4.QtNetwork import QLocalServer # self.local_server = QLocalServer() # self.local_server.listen(str(id(self))) if self.pyqt_api: env.append('PYQT_API=%d' % self.pyqt_api) env.append('IGNORE_SIP_SETAPI_ERRORS=%s' % self.ignore_sip_setapi_errors) # User Module Deleter if self.is_interpreter: env.append('UMD_ENABLED=%r' % self.umd_enabled) env.append('UMD_NAMELIST=%s' % ','.join(self.umd_namelist)) env.append('UMD_VERBOSE=%r' % self.umd_verbose) # IPython kernel env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel) pathlist = [] # Fix encoding with custom "sitecustomize.py" scpath = osp.dirname(osp.abspath(__file__)) pathlist.append(scpath) # Adding Spyder path pathlist += self.path # Adding path list to PYTHONPATH environment variable add_pathlist_to_PYTHONPATH(env, pathlist) #-------------------------Python specific------------------------------- self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), self.write_error) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self, SIGNAL('finished()'), self.dialog_manager.close_all) self.connect(self.terminate_button, SIGNAL("clicked()"), self.process.terminate) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) #-------------------------Python specific------------------------------- # Fixes for our Mac app: # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app, # but their values are messing sys.path for external interpreters # (e.g. EPD) so we need to remove them from the environment. # 2. Add this file's dir to PYTHONPATH. This will make every external # interpreter to use our sitecustomize script. # 3. Remove PYTHONOPTIMIZE from env so that we can have assert # statements working with our interpreters (See Issue 1281) if sys.platform == 'darwin' and 'Spyder.app' in __file__: env.append('SPYDER_INTERPRETER=%s' % self.pythonexecutable) if 'Spyder.app' not in self.pythonexecutable: env = [p for p in env if not (p.startswith('PYTHONPATH') or \ p.startswith('PYTHONHOME'))] # 1. env.append('PYTHONPATH=%s' % osp.dirname(__file__)) # 2. env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')] # 3. self.process.setEnvironment(env) self.process.start(self.pythonexecutable, p_args) #-------------------------Python specific------------------------------- running = self.process.waitForStarted(3000) self.set_running_state(running) if not running: QMessageBox.critical( self, _("Error"), _("A Python or IPython Console failed to start!")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process def finished(self, exit_code, exit_status): """Reimplement ExternalShellBase method""" ExternalShellBase.finished(self, exit_code, exit_status) self.introspection_socket = None #=============================================================================== # Input/Output #=============================================================================== def write_error(self): if os.name == 'nt': #---This is apparently necessary only on Windows (not sure though): # emptying standard output buffer before writing error output self.process.setReadChannel(QProcess.StandardOutput) if self.process.waitForReadyRead(1): self.write_output() self.shell.write_error(self.get_stderr()) QApplication.processEvents() def send_to_process(self, text): if not self.is_running(): return if not isinstance(text, basestring): text = unicode(text) if self.install_qt_inputhook and self.introspection_socket is not None: communicate(self.introspection_socket, "toggle_inputhook_flag(True)") # # Socket-based alternative (see input hook in sitecustomize.py): # while self.local_server.hasPendingConnections(): # self.local_server.nextPendingConnection().write('go!') if text.startswith(('%', '!')): text = 'evalsc(r"%s")\n' % text if not text.endswith('\n'): text += '\n' self.process.write(locale_codec.fromUnicode(text)) self.process.waitForBytesWritten(-1) # Eventually write prompt faster (when hitting Enter continuously) # -- necessary/working on Windows only: if os.name == 'nt': self.write_error() def keyboard_interrupt(self): if self.introspection_socket is not None: communicate(self.introspection_socket, "thread.interrupt_main()") def quit_monitor(self): if self.introspection_socket is not None: try: write_packet(self.introspection_socket, "thread.exit()") except socket.error: pass #=============================================================================== # Globals explorer #=============================================================================== def toggle_globals_explorer(self, state): if self.stand_alone is not None: self.splitter.setSizes([1, 1 if state else 0]) self.namespacebrowser_button.setChecked(state) if state and self.namespacebrowser is not None: self.namespacebrowser.refresh_table() def splitter_moved(self, pos, index): self.namespacebrowser_button.setChecked(self.splitter.sizes()[1]) #=============================================================================== # Misc. #=============================================================================== def set_current_working_directory(self): """Set current working directory""" cwd = self.shell.get_cwd() self.emit(SIGNAL('redirect_stdio(bool)'), False) directory = getexistingdirectory(self, _("Select directory"), cwd) if directory: self.shell.set_cwd(directory) self.emit(SIGNAL('redirect_stdio(bool)'), True) def show_env(self): """Show environment variables""" get_func = self.shell.get_env set_func = self.shell.set_env self.dialog_manager.show(RemoteEnvDialog(get_func, set_func)) def show_syspath(self): """Show sys.path contents""" editor = DictEditor() editor.setup(self.shell.get_syspath(), title="sys.path", readonly=True, width=600, icon='syspath.png') self.dialog_manager.show(editor)
class ExternalSystemShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = TerminalWidget def __init__(self, parent=None, wdir=None, path=[], light_background=True, menu_actions=None, show_buttons_inside=True, show_elapsed_time=True): ExternalShellBase.__init__(self, parent=parent, fname=None, wdir=wdir, history_filename='.history', light_background=light_background, menu_actions=menu_actions, show_buttons_inside=show_buttons_inside, show_elapsed_time=show_elapsed_time) # Additional python path list self.path = path # For compatibility with the other shells that can live in the external # console self.is_ipykernel = False self.connection_file = None def get_icon(self): return get_icon('cmdprompt.png') def create_process(self): self.shell.clear() self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe) env = [unicode(_path) for _path in self.process.systemEnvironment()] add_pathlist_to_PYTHONPATH(env, self.path) self.process.setEnvironment(env) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) # Shell arguments if os.name == 'nt': p_args = ['/Q'] else: p_args = ['-i'] if self.arguments: p_args.extend(shell_split(self.arguments)) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) if os.name == 'nt': self.process.start('cmd.exe', p_args) else: # Using bash: self.process.start('bash', p_args) self.send_to_process("""PS1="\u@\h:\w> "\n""") running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process #=============================================================================== # Input/Output #=============================================================================== def transcode(self, bytes): if os.name == 'nt': return encoding.transcode(str(bytes.data()), 'cp850') else: return ExternalShellBase.transcode(self, bytes) def send_to_process(self, text): if not isinstance(text, basestring): text = unicode(text) if text[:-1] in ["clear", "cls", "CLS"]: self.shell.clear() self.send_to_process(os.linesep) return if not text.endswith('\n'): text += '\n' if os.name == 'nt': self.process.write(text.encode('cp850')) else: self.process.write(locale_codec.fromUnicode(text)) self.process.waitForBytesWritten(-1) def keyboard_interrupt(self): # This does not work on Windows: # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe) self.send_ctrl_to_process('c')
class Mplayer(QDialog): REVENIR, PAS_PRECEDENT_SUIVANT, PRECEDENT_SUIVANT, CURSEUR_SUR_UNE_LIGNE,\ CURSEUR_A_PART, PARCOURIR, PAS_PARCOURIR, LIST, RATIO = range(9) HAUTEUR, LARGEUR = range(2) def __init__(self, cheminVideo=[], taille=(250,225), choixWidget=(RATIO, REVENIR, PAS_PRECEDENT_SUIVANT,CURSEUR_SUR_UNE_LIGNE,PAS_PARCOURIR,LIST), debutFin=(0,0), cheminMPlayer=None, barreTaches=None, facteurLimitant=HAUTEUR, cheminParcourir=None, parent=None): """widget mplayer""" QDialog.__init__(self, parent) #=== Paramètres généraux ===# self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle(_(u"Player vidéo")) #On réduit la marge pour gagner de l'espace self.setContentsMargins(0,0,0,0) self.systeme = os.name ### Quand EKD windows est installé, le chemin des dépendances sont ########### ### positionnées dans les variables d'environnement donc pas besoin de ####### ### collecter le chemin des ces dépendances ################################## self.cheminMPlayer = "mplayer" ############################################################################## # liste de chemins vidéos if type(cheminVideo) != list : self.listeVideos=[cheminVideo] else : self.listeVideos = cheminVideo # est-ce que la vidéo est lue? self.estLue=False # est-ce que la vidéo est en pause? self.estEnPause=False self.debutFin = debutFin # Nom du fichier courant (le self n'est pas encore utile) txtParDefaut = u"Pas de fichier lu" if self.listeVideos.__len__()!=0: self.fichierCourant = [txtParDefaut, self.listeVideos[0]] else: self.fichierCourant = [txtParDefaut, ""] # Barre des tâches de la fenêtre self.barreTaches = barreTaches # Taille de la vidéo self.tailleLargeur=taille[0] self.tailleHauteur=taille[1] # paramètres des boutons-icones iconTaille=22 flat=1 # Pour récupérer le temps courant depuis certains cadre self.temps = 0 self.dureeTimer = 10 # temps en ms ############################################################################################################################### #Pour être plus précis lors de la lecture, on prend comme unité la miliseconde. ###################### ## Il faut donc utiliser une echelle 1000 fois plus grande pour les unités du slider self.echelle=1000 ############################################################################################################################### # Permet de récupérer la durée de la vidéo depuis une instance de la classe # Sert dans certains cadres self.dureeVideo = 0 # Chemin sur lequel peut s'ouvrir la boite de dialogue de fichier # associée au bouton parcourir self.cheminPourBoutonParcourir = cheminParcourir self.taille = taille debug("self.taille avant lecture : %s %s" % (self.taille, type(self.taille))) #=== Widgets ===# self.icone_lire=QIcon("Icones" + os.sep + "player_play.png") self.icone_pause=QIcon("Icones" + os.sep + "player_pause.png") self.icone_arret=QIcon("Icones" + os.sep + "player_stop.png") if Mplayer.REVENIR in choixWidget: self.bout_revenir = QPushButton(u"Revenir") self.bout_revenir.setIcon(QIcon("Icones" + os.sep + "revenir.png")) if Mplayer.PARCOURIR in choixWidget: self.bout_ouvVideo = QPushButton(u"Parcourir...") if Mplayer.PRECEDENT_SUIVANT in choixWidget: self.bout_prec = QPushButton(QIcon("Icones" + os.sep + "player_rew.png"),"") self.bout_prec.setIconSize(QSize(iconTaille, iconTaille)) self.bout_prec.setFlat(flat) self.bout_suivant = QPushButton(QIcon("Icones" + os.sep + "player_fwd.png"),"") self.bout_suivant.setIconSize(QSize(iconTaille, iconTaille)) self.bout_suivant.setFlat(flat) self.LISTW=False if Mplayer.LIST in choixWidget : self.LISTW = True self.listFichiers = QComboBox() self.listFichiers.hide() self.setListeVideo() self.bout_LectPause = QPushButton(self.icone_lire,"") self.bout_LectPause.setIconSize(QSize(iconTaille, iconTaille)) self.bout_LectPause.setFlat(flat) self.bout_Arret = QPushButton(self.icone_arret,"") self.bout_Arret.setIconSize(QSize(iconTaille, iconTaille)) self.bout_Arret.setFlat(flat) # widget qui contiendra la vidéo self.cibleVideo = DisplayVid(self) # par défaut le widget-cible est noir color = QColor(0, 0, 0) self.cibleVideo.setAutoFillBackground(True) self.cibleVideo.setPalette(QPalette(color)) self.cibleVideo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.cibleVideo.setFixedHeight(self.taille[1]) self.cibleVideo.setToolTip(self.fichierCourant[0]) #Choix de l'aspect ratio de la vidéo if Mplayer.RATIO in choixWidget : self.conf = QGroupBox() self.conf.setContentsMargins(0,0,0,0) self.conf.setMinimumSize(QSize(self.tailleLargeur, 0)) self.conf.setObjectName("conf") self.verticalLayout = QHBoxLayout(self.conf) self.verticalLayout.setObjectName("verticalLayout") self.choicenorm = QRadioButton(self.conf) self.choicenorm.setObjectName("choicenorm") self.verticalLayout.addWidget(self.choicenorm) self.choicewide = QRadioButton(self.conf) self.choicewide.setObjectName("choicewide") self.verticalLayout.addWidget(self.choicewide) self.choiceone = QRadioButton(self.conf) self.choiceone.setObjectName("choiceone") self.verticalLayout.addWidget(self.choiceone) self.choicenorm.setText("4:3") self.choicewide.setText("16:9") self.choiceone.setText("w:h") # Checked le ratio de la vidéo if self.listeVideos.__len__()!=0: self.changeRatio(self.listeVideos[0]) else : self.setRatio(4.0/3.0) if Mplayer.RATIO in choixWidget : self.choicenorm.setChecked(True) self.slider = QSlider(Qt.Horizontal) self.slider.setEnabled(True) self.mplayerProcess = QProcess(self) self.timer = QTimer(self) self.tempsChrono = TracerChrono() #=== mise-en-page/plan ===# mhbox = QHBoxLayout() vbox = QVBoxLayout() vbox.addWidget(self.cibleVideo) if Mplayer.RATIO in choixWidget : vbox.addWidget(self.conf) hbox = QHBoxLayout() if Mplayer.REVENIR in choixWidget: hbox.addWidget(self.bout_revenir) if Mplayer.PARCOURIR in choixWidget: hbox.addWidget(self.bout_ouvVideo) hbox.addWidget(self.bout_LectPause) hbox.addWidget(self.bout_Arret) if Mplayer.PRECEDENT_SUIVANT in choixWidget: hbox.addWidget(self.bout_prec) hbox.addWidget(self.bout_suivant) hbox.addWidget(self.tempsChrono) if Mplayer.CURSEUR_A_PART not in choixWidget: hbox.addWidget(self.slider) vbox.addLayout(hbox) if Mplayer.CURSEUR_A_PART in choixWidget: hbox.setAlignment(Qt.AlignLeft) hbox = QHBoxLayout() hbox.addWidget(self.slider) vbox.addLayout(hbox) # Liste fichier dans combobox if self.LISTW : hbox = QHBoxLayout() hbox.addWidget(self.listFichiers) vbox.addLayout(hbox) mhbox.addLayout(vbox) self.setLayout(mhbox) #=== connexion des widgets à des fonctions ===# if Mplayer.REVENIR in choixWidget: self.connect(self.bout_revenir, SIGNAL('clicked()'), SLOT('close()')) if Mplayer.PARCOURIR in choixWidget: self.connect(self.bout_ouvVideo, SIGNAL('clicked()'), self.ouvrirVideo) if Mplayer.PRECEDENT_SUIVANT in choixWidget: self.connect(self.bout_prec, SIGNAL('clicked()'), self.precedent) self.connect(self.bout_suivant, SIGNAL('clicked()'), self.suivant) #Ajouté le 08/11/2009 - Liste des fichiers dans une combobox if self.LISTW : self.connect(self.listFichiers, SIGNAL('currentIndexChanged(int)'), self.changeVideo) self.connect(self.bout_LectPause, SIGNAL('clicked()'), self.lectPause) self.connect(self.bout_Arret, SIGNAL('clicked()'), self.arretMPlayer) self.connect(self.mplayerProcess, SIGNAL('readyReadStandardOutput()'), self.recupSortie) self.connect(self.mplayerProcess, SIGNAL('finished(int,QProcess::ExitStatus)'), self.finVideo) self.connect(self.timer, SIGNAL('timeout()'), self.sonderTempsActuel) self.connect(self.slider, SIGNAL('sliderMoved(int)'), self.changerTempsCurseur) self.connect(self.cibleVideo, SIGNAL('changeSize'), self.sizeMplayer) if Mplayer.RATIO in choixWidget : self.connect(self.choicenorm, SIGNAL("clicked(bool)"), self.defRatio) self.connect(self.choicewide, SIGNAL("clicked(bool)"), self.defRatio) self.connect(self.choiceone, SIGNAL("clicked(bool)"), self.defRatio) def setListeVideo(self) : self.referenceVideo = [] self.listFichiers.clear() for vid in self.listeVideos : self.referenceVideo.append(vid) self.listFichiers.addItem(os.path.basename(vid)) if self.listeVideos.__len__() > 1 : self.listFichiers.show() def setAudio(self,au) : if au : self.cibleVideo.hide() if "conf" in self.__dict__ : self.conf.hide() else : self.cibleVideo.show() if "conf" in self.__dict__ : self.conf.show() def changeVideo(self, index) : self.arretMPlayer() if index >= 0 : # Condition ajoutée pour éviter une erreure de dépassement de range dans la liste. self.listeVideos = self.referenceVideo[index] self.listFichiers.setCurrentIndex(index) def defRatio(self, state=0) : if state : if self.choicenorm.isChecked() : self.setRatio(4.0/3.0) if self.choicewide.isChecked() : self.setRatio(16.0/9.0) if self.choiceone.isChecked() : try : dim=getVideoSize(unicode(self.listeVideos[0])) self.setRatio(dim[0]/dim[1]) except : None self.defRatio() else : self.adjustSize() def setRatio(self,ratio) : self.ratio = ratio self.sizeMplayer() def changeRatio(self,video) : rv = getVideoRatio(video) if rv[0]==0.0 and type(rv[1])==float : rat = rv[1] else : rat = rv[0] if rat > 1.7 : if "choicewide" in self.__dict__ : self.choicewide.setChecked(True) self.setRatio(16.0/9.0) elif rat > 1.3 and rat <= 1.7 : if "choicenorm" in self.__dict__ : self.choicenorm.setChecked(True) self.setRatio(4.0/3.0) elif rat < 1.3 and rat != 0.0 : if "choiceone" in self.__dict__ : self.choiceone.setChecked(True) dim=getVideoSize(video) self.setRatio(dim[0]/dim[1]) else : if "choicenorm" in self.__dict__ : self.choicenorm.setChecked(True) self.setRatio(4.0/3.0) def sizeMplayer(self) : self.cibleVideo.setFixedHeight(int(self.cibleVideo.width()/self.ratio)) def ouvrirVideo(self): """Ouverture de la boîte de dialogue de fichiers""" txt = u"Fichiers vidéo" if self.cheminPourBoutonParcourir: chemin = self.cheminPourBoutonParcourir else: try: chemin = EkdConfig.get('general','video_input_path').decode("UTF8") except: chemin = os.path.expanduser('~') liste=QFileDialog.getOpenFileNames(None, u"Ouvrir", chemin, "%s (*.avi *.mpg *.mpeg *.mjpeg *.flv *.mp4 *.ogg *.vob *.mov *.wmv *.3gp *.h264)\n*" %txt) if not liste: return self.listeVideos = liste self.changeRatio(unicode(self.listeVideos[0])) chemin = unicode(self.listeVideos[0]) EkdConfig.set('general','video_input_path',os.path.dirname(chemin).encode("UTF8")) def setVideos(self, videos) : '''Définie proprement la liste des vidéos à jouer''' if type(videos) != list : self.listeVideos = [videos] else : self.listeVideos = videos if self.LISTW and videos.__len__() > 1 : self.setListeVideo() elif self.LISTW : self.listFichiers.hide() def demarrerMPlayer(self): """démarrage de mplayer avec les arguments choisis""" if self.estLue: return True args = QStringList() # Liste Qt qui contiendra les options de mplayer # Ajout d'options à liste: args << "-option" # mplayer fonctionnera comme un terminal dans ce script args << "-slave" # on ne veut pas avoir des commentaires sans grand intérêt args << "-quiet" # Sous linux, aucun driver n'a été nécessaire et pas de manip pour Wid :) if self.systeme=='posix': # try - except? # la fenêtre de mplayer restera attaché à la fenêtre # wid prend en valeur le nombre identifiant le widget (celui qui contiendra la vidéo) args << "-wid" << QString.number(self.cibleVideo.winId()) # Objet QString car args est une liste de ch de caractères settings = QSettings() videoOutput = settings.value("vo", QVariant('')).toString() if videoOutput: args << '-vo' << videoOutput # Sous windows else: # reinterpret_cast<qlonglong> obligatoire, winId() ne se laissant pas convertir gentiment ;) args << "-wid" << self.cibleVideo.winId().__hex__() args << "-vo" << "directx:noaccel" #args << "-vo" << "gl" # alternative # chemin de la vidéo args << self.listeVideos if PYQT_VERSION_STR >= "4.1.0": # mode de canal: on fusionne le canal de sortie normal (stdout) et celui des erreurs (stderr) self.mplayerProcess.setProcessChannelMode(QProcess.MergedChannels) # démarrage de mplayer (en tenant compte des arguments définis ci-dessus) # comme un nouveau processus self.mplayerProcess.start(self.cheminMPlayer, args) # au cas où mplayer ne démarrerait pas au bout de 3 sec (ex. problème de codec) if not self.mplayerProcess.waitForStarted(3000): QMessageBox.critical(self, u"Avertissement", u"Bogue au lancement de la vidéo avec mplayer") return False # donne le temps toutes les x secondes self.timer.start(self.dureeTimer) self.estLue = True return True def recupSortie(self): """récupère les lignes d'information émises par QProcess (mplayerProcess) et en tire les conséquences""" while self.mplayerProcess.canReadLine(): # renvoie True si une ligne complète peut être lue à partir du système # stocker l'ensemble des bits d'une ligne tampon=QByteArray(self.mplayerProcess.readLine()) # readline: lit une ligne ascii à partir du système # On vérifie si on a eu des réponses if tampon.startsWith("Playing"): # On récupère les infos de base ('$ mplayer -input cmdlist' pour avoir la liste complète - file:///usr/share/doc/mplayer-doc/tech/slave.txt.gz pour plus de détails) self.mplayerProcess.write("get_video_resolution\n") # récupère la résolution de la vidéo self.mplayerProcess.write("get_time_length\n") # Nouveau fichier chargé -> on récupère son nom ind = tampon.length() - 2 # suppression du '.' à la fin tampon.remove(ind,ind) tampon.remove(0, 8) # vire Playing tampon.replace(QByteArray("\n"), QByteArray("")) tampon.replace(QByteArray("\r"), QByteArray("")) try: # Tour de passe-passe pour ne pas avoir de problème d'accents # Condition pour détection windows if os.name == 'nt': self.fichierCourant[1]=unicode(QString(tampon)) # Condition pour détection Linux ou MacOSX elif os.name in ['posix', 'mac']: self.fichierCourant[1]=unicode(QString(tampon)).encode("Latin1").decode("UTF8") except UnicodeEncodeError, e: debug(e) self.fichierCourant[1]="?" self.cibleVideo.setToolTip(self.fichierCourant[1]) if self.barreTaches is not None: self.barreTaches.showMessage(self.fichierCourant[1]) # réponse à get_video_resolution : ANS_VIDEO_RESOLUTION='<width> x <height>' if tampon.startsWith("ANS_VIDEO_RESOLUTION"): # retourne True si l'ensemble de bits démarre avec "..." debug("tampon : %s" % tampon) # ex. -> ANS_VIDEO_RESOLUTION='352 x 288' tampon.remove(0, 21) # suppression des 21 1er caract -> '352 x 288' tampon.replace(QByteArray("'"), QByteArray("")) # -> 352 x 288 tampon.replace(QByteArray(" "), QByteArray("")) # -> 352x288 tampon.replace(QByteArray("\n"), QByteArray("")) # -> 352x288 # retour chariot unix tampon.replace(QByteArray("\r"), QByteArray("")) # -> 352x288 # retour chariot windows #print "-----tampon.indexOf('x') :", tampon.indexOf('x'), type(tampon.indexOf('x')) sepIndex = tampon.indexOf('x') # récupère la position de 'x' # 3 <type 'int'> #print "-----tampon.left(sepIndex).toInt():", tampon.left(sepIndex).toInt(), type(tampon.left(sepIndex).toInt()) resX = tampon.left(sepIndex).toInt()[0] # -> 352 # (352, True) <type 'tuple'> #print "-----tampon.mid(sepIndex+1).toInt() :", tampon.mid(sepIndex+1).toInt(), type(tampon.mid(sepIndex+1).toInt()) resY = tampon.mid(sepIndex+1).toInt()[0] # -> 288 # (288, True) <type 'tuple'> # on définit les nouvelles dimensions de l'image du widget-mplayer. # try pour éviter les bogues sur les fichiers audio (sans dimension d'image)!!! #try: if resX!=0 or resY!=0: debug( "ratio : %s - %s" % (self.ratio, type(self.ratio))) else: debug("fichier audio") # réponse à get_time_length : ANS_LENGTH=xx.yy elif tampon.startsWith("ANS_LENGTH"): debug("tampon : %s" % tampon) # -> ANS_LENGTH=279.38 tampon.remove(0, 11) # vire ANS_LENGTH= tampon.replace(QByteArray("'"), QByteArray("")) tampon.replace(QByteArray(" "), QByteArray("")) tampon.replace(QByteArray("\n"), QByteArray("")) tampon.replace(QByteArray("\r"), QByteArray("")) # -> 279.38 #print "-----tampon.toFloat() :", tampon.toFloat(), type(tampon.toFloat()) tempsMax = tampon.toFloat()[0] # (279.3800048828125, True) <type 'tuple'> self.dureeVideo = tempsMax ## Modifié le 28/05/2009 : On augmente la précision du slider #self.slider.setMaximum(tempsMax) # déf du domaine de valeur du curseur self.slider.setMaximum(tempsMax*self.echelle) # ATTENTION J'AI COMMENTE CETTE LIGNE !!! #self.slider.setMaximum(tempsMax) # réponse à get_time_pos : ANS_TIME_POSITION=xx.y elif tampon.startsWith("ANS_TIME_POSITION"): #print "tampon :",tampon # -> ANS_TIME_POSITION=1.4 (temps courant) tampon.remove(0, 18) # vire ANS_TIME_POSITION= tampon.replace(QByteArray("'"), QByteArray("")) tampon.replace(QByteArray(" "), QByteArray("")) tampon.replace(QByteArray("\n"), QByteArray("")) tampon.replace(QByteArray("\r"), QByteArray("")) #print "-----tampon.toFloat() :", tampon.toFloat(), type(tampon.toFloat()) tempsCourant = tampon.toFloat()[0] # (1.3999999761581421, True) <type 'tuple'> # récupération du temps courant: utile dans certains cadres self.temps = tempsCourant # Programmer un arrêt. Utile pour les aperçus temps = float("%.1f" %self.temps) if self.debutFin!=(0,0) and self.debutFin[1]==temps: self.arretMPlayer() return self.slider.setValue(tempsCourant*self.echelle) ############################################################################# self.changerTempsChrono(tempsCourant) # modifier le chrono du bouton
class ProfilerWidget(QWidget): """ Profiler widget """ DATAPATH = get_conf_path('.profiler.results') VERSION = '0.0.1' def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.setWindowTitle("Profiler") self.output = None self.error_output = None self.filecombo = PythonModulesComboBox(self) self.start_button = create_toolbutton(self, icon=get_icon('run.png'), text=translate('Profiler', "Profile"), tip=translate('Profiler', "Run profiler"), triggered=self.start, text_beside_icon=True) self.stop_button = create_toolbutton(self, icon=get_icon('terminate.png'), text=translate('Profiler', "Stop"), tip=translate('Profiler', "Stop current profiling"), text_beside_icon=True) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) # FIXME: The combobox emits this signal on almost any event # triggering show_data() too early, too often. browse_button = create_toolbutton(self, icon=get_icon('fileopen.png'), tip=translate('Profiler', 'Select Python script'), triggered=self.select_file) self.datelabel = QLabel() self.log_button = create_toolbutton(self, icon=get_icon('log.png'), text=translate('Profiler', "Output"), text_beside_icon=True, tip=translate('Profiler', "Show program's output"), triggered=self.show_log) self.datatree = ProfilerDataTree(self) self.collapse_button = create_toolbutton(self, icon=get_icon('collapse.png'), triggered=lambda dD=-1:self.datatree.change_view(dD), tip='Collapse one level up') self.expand_button = create_toolbutton(self, icon=get_icon('expand.png'), triggered=lambda dD=1:self.datatree.change_view(dD), tip='Expand one level down') hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.collapse_button) hlayout2.addWidget(self.expand_button) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.datatree) self.setLayout(layout) self.process = None self.set_running_state(False) if not is_profiler_installed(): for widget in (self.datatree, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) if os.name == 'nt' \ and programs.is_module_installed("profile"): # The following is a comment from the pylint plugin: # Module is installed but script is not in PATH # (AFAIK, could happen only on Windows) text = translate('Profiler', 'Profiler script was not found. Please add "%s" to PATH.') text = unicode(text) % os.path.join(sys.prefix, "Scripts") else: text = translate('Profiler', ('Please install the modules '+ '<b>profile</b> and <b>pstats</b>:')) # FIXME: need the actual website url = 'http://www.python.org' text += ' <a href=%s>%s</a>' % (url, url) self.datelabel.setText(text) else: pass # self.show_data() def analyze(self, filename): if not is_profiler_installed(): return filename = unicode(filename) # filename is a QString instance self.kill_if_running() #index, _data = self.get_data(filename) index=None # FIXME: storing data is not implemented yet if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count()-1) else: self.filecombo.setCurrentIndex(self.filecombo.findText(filename)) self.filecombo.selected() if self.filecombo.is_valid(): self.start() def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename = QFileDialog.getOpenFileName(self, translate('Profiler', "Select Python script"), os.getcwdu(), translate('Profiler', "Python scripts")+" (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def show_log(self): if self.output: TextEditor(self.output, title=translate('Profiler', "Profiler output"), readonly=True, size=(700, 500)).exec_() def show_errorlog(self): if self.error_output: TextEditor(self.error_output, title=translate('Profiler', "Profiler output"), readonly=True, size=(700, 500)).exec_() def start(self): self.datelabel.setText('Profiling, please wait...') filename = unicode(self.filecombo.currentText()) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(os.path.dirname(filename)) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) self.output = '' self.error_output = '' p_args = [os.path.basename(filename)] # FIXME: Use the system path to 'python' as opposed to hardwired p_args = ['-m', PROFILER_PATH, '-o', self.DATAPATH, os.path.basename(filename)] self.process.start('python', p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, translate('Profiler', "Error"), translate('Profiler', "Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) bytes = QByteArray() while self.process.bytesAvailable(): if error: bytes += self.process.readAllStandardError() else: bytes += self.process.readAllStandardOutput() text = unicode( QString.fromLocal8Bit(bytes.data()) ) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) self.show_errorlog() # If errors occurred, show them. self.output = self.error_output + self.output # FIXME: figure out if show_data should be called here or # as a signal from the combobox self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = unicode(self.filecombo.currentText()) if not filename: return self.datatree.load_data(self.DATAPATH) self.datatree.show_tree() text_style = "<span style=\'color: #444444\'><b>%s </b></span>" date_text = text_style % time.strftime("%d %b %Y %H:%M",time.localtime()) self.datelabel.setText(date_text)
class serverwidget(QtGui.QFrame): def __init__(self, aname, apath): super(serverwidget, self).__init__() self.process = None self.path = apath self.name = aname self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken) title = QtGui.QLabel(self.name) startbutton = QtGui.QPushButton('START') killbutton = QtGui.QPushButton('TERMINATE') pingbutton = QtGui.QPushButton('PING') self.textfield = QtGui.QTextEdit() self.textfield.setReadOnly(True) startbutton.pressed.connect(self.start_server) killbutton.pressed.connect(self.kill_server) pingbutton.pressed.connect(self.ping_server) sublayout = QtGui.QVBoxLayout() sublayout.addWidget(title) sublayout.addWidget(startbutton) sublayout.addWidget(killbutton) sublayout.addWidget(pingbutton) sublayout.addWidget(self.textfield) self.setLayout(sublayout) def start_server(self): if self.process is None: self.process = QProcess() self.process.readyReadStandardOutput.connect(self.read_output) self.process.started.connect( lambda: self.write_message('Server started')) self.process.finished.connect( lambda: self.write_message('Server stopped')) self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.setWorkingDirectory(os.path.dirname(self.path)) self.process.start('python', [self.path]) else: self.textfield.append( 'Cannot start "{:}", as it is already running'.format( self.name)) def kill_server(self): if self.process is not None: self.process.terminate() self.process = None else: self.textfield.append( 'Cannot terminate "{:}", as it is not running'.format( self.name)) def ping_server(self): if self.process is not None: state = self.process.state() msg = "PING: " if state == 0: msg += 'Process died' elif state == 1: msg += 'Process is starting up' elif state == 2: msg += 'Process is alive' self.textfield.append(msg) cnx = labrad.connect() labradserver = eval('cnx.{:}'.format(self.name)) msg = 'PING: Labradserver is ' + labradserver.echo('alive') self.textfield.append(msg) cnx.disconnect() else: self.textfield.append('Cannot ping a server that is not started') def read_output(self): data = self.process.readAllStandardOutput() self.textfield.append(str(data)) def write_message(self, message): self.textfield.append(message)
class PylintWidget(QWidget): """ Pylint widget """ DATAPATH = get_conf_path('.pylint.results') VERSION = '1.0.2' def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.output = None self.error_output = None self.max_entries = max_entries self.data = [self.VERSION] if osp.isfile(self.DATAPATH): try: data = cPickle.load(file(self.DATAPATH)) if data[0] == self.VERSION: self.data = data except EOFError: pass self.filecombo = PythonModulesComboBox(self) if self.data: self.remove_obsolete_items() self.filecombo.addItems(self.get_filenames()) self.start_button = create_toolbutton(self, get_icon('run.png'), translate('Pylint', "Analyze"), tip=translate('Pylint', "Run analysis"), triggered=self.start) self.stop_button = create_toolbutton(self, get_icon('terminate.png'), translate('Pylint', "Stop"), tip=translate('Pylint', "Stop current analysis")) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) browse_button = create_toolbutton(self, get_icon('fileopen.png'), tip=translate('Pylint', 'Select Python script'), triggered=self.select_file) self.ratelabel = QLabel() self.datelabel = QLabel() self.log_button = create_toolbutton(self, get_icon('log.png'), translate('Pylint', "Output"), tip=translate('Pylint', "Complete Pylint output"), triggered=self.show_log) self.treewidget = ResultsTree(self) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.ratelabel) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.treewidget) self.setLayout(layout) self.process = None self.set_running_state(False) if not is_pylint_installed(): for widget in (self.treewidget, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) text = translate('Pylint', 'Please install <b>pylint</b>:') url = 'http://www.logilab.fr' text += ' <a href=%s>%s</a>' % (url, url) self.ratelabel.setText(text) else: self.show_data() def analyze(self, filename): if not is_pylint_installed(): return filename = unicode(filename) # filename is a QString instance self.kill_if_running() index, _data = self.get_data(filename) if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count()-1) else: self.filecombo.setCurrentIndex(index) self.filecombo.selected() if self.filecombo.is_valid(): self.start() def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename = QFileDialog.getOpenFileName(self, translate('Pylint', "Select Python script"), os.getcwdu(), translate('Pylint', "Python scripts")+" (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def remove_obsolete_items(self): """Removing obsolete items""" self.data = [self.VERSION] + \ [(filename, data) for filename, data in self.data[1:] if is_module_or_package(filename)] def get_filenames(self): return [filename for filename, _data in self.data[1:]] def get_data(self, filename): filename = osp.abspath(filename) for index, (fname, data) in enumerate(self.data[1:]): if fname == filename: return index, data else: return None, None def set_data(self, filename, data): filename = osp.abspath(filename) index, _data = self.get_data(filename) if index is not None: self.data.pop(index) self.data.append( (filename, data) ) self.save() def set_max_entries(self, max_entries): self.max_entries = max_entries self.save() def save(self): while len(self.data) > self.max_entries+1: self.data.pop(1) cPickle.dump(self.data, file(self.DATAPATH, 'w')) def show_log(self): if self.output: TextEditor(self.output, title=translate('Pylint', "Pylint output"), readonly=True, size=(700, 500)).exec_() def start(self): filename = unicode(self.filecombo.currentText()) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(osp.dirname(filename)) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) self.output = '' self.error_output = '' p_args = [osp.basename(filename)] self.process.start(PYLINT_PATH, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, translate('Pylint', "Error"), translate('Pylint', "Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) bytes = QByteArray() while self.process.bytesAvailable(): if error: bytes += self.process.readAllStandardError() else: bytes += self.process.readAllStandardOutput() text = unicode( QString.fromLocal8Bit(bytes.data()) ) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) if not self.output: return # Convention, Refactor, Warning, Error results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} txt_module = '************* Module ' module = '' # Should not be needed - just in case something goes wrong for line in self.output.splitlines(): if line.startswith(txt_module): # New module module = line[len(txt_module):] continue for prefix in results: if line.startswith(prefix): break else: continue i1 = line.find(':') if i1 == -1: continue i2 = line.find(':', i1+1) if i2 == -1: continue line_nb = line[i1+1:i2].strip() if not line_nb: continue line_nb = int(line_nb) message = line[i2+1:] item = (module, line_nb, message) results[line[:i1+1]].append(item) # Rate rate = None txt_rate = 'Your code has been rated at ' i_rate = self.output.find(txt_rate) if i_rate > 0: i_rate_end = self.output.find('/10', i_rate) if i_rate_end > 0: rate = self.output[i_rate+len(txt_rate):i_rate_end] # Previous run previous = '' if rate is not None: txt_prun = 'previous run: ' i_prun = self.output.find(txt_prun, i_rate_end) if i_prun > 0: i_prun_end = self.output.find('/10', i_prun) previous = self.output[i_prun+len(txt_prun):i_prun_end] filename = unicode(self.filecombo.currentText()) self.set_data(filename, (time.localtime(), rate, previous, results)) self.output = self.error_output + self.output self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = unicode(self.filecombo.currentText()) if not filename: return _index, data = self.get_data(filename) if data is None: text = translate('Pylint', 'Source code has not been rated yet.') self.treewidget.clear() date_text = '' else: datetime, rate, previous_rate, results = data if rate is None: text = translate('Pylint', 'Analysis did not succeed ' '(see output for more details).') self.treewidget.clear() date_text = '' else: text_style = "<span style=\'color: #444444\'><b>%s </b></span>" rate_style = "<span style=\'color: %s\'><b>%s</b></span>" prevrate_style = "<span style=\'color: #666666\'>%s</span>" color = "#FF0000" if float(rate) > 5.: color = "#22AA22" elif float(rate) > 3.: color = "#EE5500" text = translate('Pylint', 'Global evaluation:') text = (text_style % text)+(rate_style % (color, ('%s/10' % rate))) if previous_rate: text_prun = translate('Pylint', 'previous run:') text_prun = ' (%s %s/10)' % (text_prun, previous_rate) text += prevrate_style % text_prun self.treewidget.set_results(filename, results) date_text = text_style % time.strftime("%d %b %Y %H:%M", datetime) self.ratelabel.setText(text) self.datelabel.setText(date_text)
class PylintWidget(QWidget): """ Pylint widget """ DATAPATH = get_conf_path('.pylint.results') VERSION = '1.0.2' def __init__(self, parent, max_entries=100): QWidget.__init__(self, parent) self.output = None self.error_output = None self.max_entries = max_entries self.data = [self.VERSION] if osp.isfile(self.DATAPATH): try: data = cPickle.load(file(self.DATAPATH)) if data[0] == self.VERSION: self.data = data except EOFError: pass self.filecombo = PythonModulesComboBox(self) if self.data: self.remove_obsolete_items() self.filecombo.addItems(self.get_filenames()) self.start_button = create_toolbutton(self, get_icon('run.png'), translate('Pylint', "Analyze"), tip=translate( 'Pylint', "Run analysis"), triggered=self.start) self.stop_button = create_toolbutton(self, get_icon('terminate.png'), translate('Pylint', "Stop"), tip=translate( 'Pylint', "Stop current analysis")) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.start_button.setEnabled) self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data) browse_button = create_toolbutton(self, get_icon('fileopen.png'), tip=translate( 'Pylint', 'Select Python script'), triggered=self.select_file) self.ratelabel = QLabel() self.datelabel = QLabel() self.log_button = create_toolbutton(self, get_icon('log.png'), translate('Pylint', "Output"), tip=translate( 'Pylint', "Complete Pylint output"), triggered=self.show_log) self.treewidget = ResultsTree(self) hlayout1 = QHBoxLayout() hlayout1.addWidget(self.filecombo) hlayout1.addWidget(browse_button) hlayout1.addWidget(self.start_button) hlayout1.addWidget(self.stop_button) hlayout2 = QHBoxLayout() hlayout2.addWidget(self.ratelabel) hlayout2.addStretch() hlayout2.addWidget(self.datelabel) hlayout2.addStretch() hlayout2.addWidget(self.log_button) layout = QVBoxLayout() layout.addLayout(hlayout1) layout.addLayout(hlayout2) layout.addWidget(self.treewidget) self.setLayout(layout) self.process = None self.set_running_state(False) if not is_pylint_installed(): for widget in (self.treewidget, self.filecombo, self.start_button, self.stop_button): widget.setDisabled(True) text = translate('Pylint', 'Please install <b>pylint</b>:') url = 'http://www.logilab.fr' text += ' <a href=%s>%s</a>' % (url, url) self.ratelabel.setText(text) else: self.show_data() def analyze(self, filename): if not is_pylint_installed(): return filename = unicode(filename) # filename is a QString instance self.kill_if_running() index, _data = self.get_data(filename) if index is None: self.filecombo.addItem(filename) self.filecombo.setCurrentIndex(self.filecombo.count() - 1) else: self.filecombo.setCurrentIndex(index) self.filecombo.selected() if self.filecombo.is_valid(): self.start() def select_file(self): self.emit(SIGNAL('redirect_stdio(bool)'), False) filename = QFileDialog.getOpenFileName( self, translate('Pylint', "Select Python script"), os.getcwdu(), translate('Pylint', "Python scripts") + " (*.py ; *.pyw)") self.emit(SIGNAL('redirect_stdio(bool)'), False) if filename: self.analyze(filename) def remove_obsolete_items(self): """Removing obsolete items""" self.data = [self.VERSION] + \ [(filename, data) for filename, data in self.data[1:] if is_module_or_package(filename)] def get_filenames(self): return [filename for filename, _data in self.data[1:]] def get_data(self, filename): filename = osp.abspath(filename) for index, (fname, data) in enumerate(self.data[1:]): if fname == filename: return index, data else: return None, None def set_data(self, filename, data): filename = osp.abspath(filename) index, _data = self.get_data(filename) if index is not None: self.data.pop(index) self.data.append((filename, data)) self.save() def set_max_entries(self, max_entries): self.max_entries = max_entries self.save() def save(self): while len(self.data) > self.max_entries + 1: self.data.pop(1) cPickle.dump(self.data, file(self.DATAPATH, 'w')) def show_log(self): if self.output: TextEditor(self.output, title=translate('Pylint', "Pylint output"), readonly=True, size=(700, 500)).exec_() def start(self): filename = unicode(self.filecombo.currentText()) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(osp.dirname(filename)) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.read_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), lambda: self.read_output(error=True)) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.stop_button, SIGNAL("clicked()"), self.process.kill) self.output = '' self.error_output = '' p_args = [osp.basename(filename)] self.process.start(PYLINT_PATH, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical( self, translate('Pylint', "Error"), translate('Pylint', "Process failed to start")) def set_running_state(self, state=True): self.start_button.setEnabled(not state) self.stop_button.setEnabled(state) def read_output(self, error=False): if error: self.process.setReadChannel(QProcess.StandardError) else: self.process.setReadChannel(QProcess.StandardOutput) bytes = QByteArray() while self.process.bytesAvailable(): if error: bytes += self.process.readAllStandardError() else: bytes += self.process.readAllStandardOutput() text = unicode(QString.fromLocal8Bit(bytes.data())) if error: self.error_output += text else: self.output += text def finished(self): self.set_running_state(False) if not self.output: return # Convention, Refactor, Warning, Error results = {'C:': [], 'R:': [], 'W:': [], 'E:': []} txt_module = '************* Module ' module = '' # Should not be needed - just in case something goes wrong for line in self.output.splitlines(): if line.startswith(txt_module): # New module module = line[len(txt_module):] continue for prefix in results: if line.startswith(prefix): break else: continue i1 = line.find(':') if i1 == -1: continue i2 = line.find(':', i1 + 1) if i2 == -1: continue line_nb = line[i1 + 1:i2].strip() if not line_nb: continue line_nb = int(line_nb) message = line[i2 + 1:] item = (module, line_nb, message) results[line[:i1 + 1]].append(item) # Rate rate = None txt_rate = 'Your code has been rated at ' i_rate = self.output.find(txt_rate) if i_rate > 0: i_rate_end = self.output.find('/10', i_rate) if i_rate_end > 0: rate = self.output[i_rate + len(txt_rate):i_rate_end] # Previous run previous = '' if rate is not None: txt_prun = 'previous run: ' i_prun = self.output.find(txt_prun, i_rate_end) if i_prun > 0: i_prun_end = self.output.find('/10', i_prun) previous = self.output[i_prun + len(txt_prun):i_prun_end] filename = unicode(self.filecombo.currentText()) self.set_data(filename, (time.localtime(), rate, previous, results)) self.output = self.error_output + self.output self.show_data(justanalyzed=True) def kill_if_running(self): if self.process is not None: if self.process.state() == QProcess.Running: self.process.kill() self.process.waitForFinished() def show_data(self, justanalyzed=False): if not justanalyzed: self.output = None self.log_button.setEnabled(self.output is not None \ and len(self.output) > 0) self.kill_if_running() filename = unicode(self.filecombo.currentText()) if not filename: return _index, data = self.get_data(filename) if data is None: text = translate('Pylint', 'Source code has not been rated yet.') self.treewidget.clear() date_text = '' else: datetime, rate, previous_rate, results = data if rate is None: text = translate( 'Pylint', 'Analysis did not succeed ' '(see output for more details).') self.treewidget.clear() date_text = '' else: text_style = "<span style=\'color: #444444\'><b>%s </b></span>" rate_style = "<span style=\'color: %s\'><b>%s</b></span>" prevrate_style = "<span style=\'color: #666666\'>%s</span>" color = "#FF0000" if float(rate) > 5.: color = "#22AA22" elif float(rate) > 3.: color = "#EE5500" text = translate('Pylint', 'Global evaluation:') text = (text_style % text) + (rate_style % (color, ('%s/10' % rate))) if previous_rate: text_prun = translate('Pylint', 'previous run:') text_prun = ' (%s %s/10)' % (text_prun, previous_rate) text += prevrate_style % text_prun self.treewidget.set_results(filename, results) date_text = text_style % time.strftime("%d %b %Y %H:%M", datetime) self.ratelabel.setText(text) self.datelabel.setText(date_text)
class ExternalPythonShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = ExtPythonShellWidget def __init__(self, parent=None, fname=None, wdir=None, interact=False, debug=False, path=[], python_args='', ipykernel=False, arguments='', stand_alone=None, umd_enabled=True, umd_namelist=[], umd_verbose=True, pythonstartup=None, pythonexecutable=None, monitor_enabled=True, mpl_patch_enabled=True, mpl_backend=None, ets_backend='qt4', qt_api=None, pyqt_api=0, install_qt_inputhook=True, ignore_sip_setapi_errors=False, merge_output_channels=False, colorize_sys_stderr=False, autorefresh_timeout=3000, autorefresh_state=True, light_background=True, menu_actions=None, show_buttons_inside=True, show_elapsed_time=True): assert qt_api in (None, 'pyqt', 'pyside') self.namespacebrowser = None # namespace browser widget! self.dialog_manager = DialogManager() self.stand_alone = stand_alone # stand alone settings (None: plugin) self.pythonstartup = pythonstartup self.pythonexecutable = pythonexecutable self.monitor_enabled = monitor_enabled self.mpl_patch_enabled = mpl_patch_enabled self.mpl_backend = mpl_backend self.ets_backend = ets_backend self.qt_api = qt_api self.pyqt_api = pyqt_api self.install_qt_inputhook = install_qt_inputhook self.ignore_sip_setapi_errors = ignore_sip_setapi_errors self.merge_output_channels = merge_output_channels self.colorize_sys_stderr = colorize_sys_stderr self.umd_enabled = umd_enabled self.umd_namelist = umd_namelist self.umd_verbose = umd_verbose self.autorefresh_timeout = autorefresh_timeout self.autorefresh_state = autorefresh_state self.namespacebrowser_button = None self.cwd_button = None self.env_button = None self.syspath_button = None self.terminate_button = None self.notification_thread = None ExternalShellBase.__init__(self, parent=parent, fname=fname, wdir=wdir, history_filename='.history.py', light_background=light_background, menu_actions=menu_actions, show_buttons_inside=show_buttons_inside, show_elapsed_time=show_elapsed_time) if self.pythonexecutable is None: self.pythonexecutable = get_python_executable() self.python_args = None if python_args: assert isinstance(python_args, basestring) self.python_args = python_args assert isinstance(arguments, basestring) self.arguments = arguments self.connection_file = None self.is_ipykernel = ipykernel if self.is_ipykernel: interact = False # Running our custom startup script for IPython kernels: # (see spyderlib/widgets/externalshell/start_ipython_kernel.py) self.fname = get_module_source_path( 'SMlib.widgets.externalshell', 'start_ipython_kernel.py') self.shell.set_externalshell(self) self.toggle_globals_explorer(False) self.interact_action.setChecked(interact) self.debug_action.setChecked(debug) self.introspection_socket = None self.is_interpreter = fname is None if self.is_interpreter: self.terminate_button.hide() # Additional python path list self.path = path self.shell.path = path def set_introspection_socket(self, introspection_socket): self.introspection_socket = introspection_socket if self.namespacebrowser is not None: settings = self.namespacebrowser.get_view_settings() communicate(introspection_socket, 'set_remote_view_settings()', settings=[settings]) def set_autorefresh_timeout(self, interval): if self.introspection_socket is not None: try: communicate(self.introspection_socket, "set_monitor_timeout(%d)" % interval) except socket.error: pass def closeEvent(self, event): self.quit_monitor() ExternalShellBase.closeEvent(self, event) def get_toolbar_buttons(self): ExternalShellBase.get_toolbar_buttons(self) if self.namespacebrowser_button is None \ and self.stand_alone is not None: self.namespacebrowser_button = create_toolbutton(self, text=_("Variables"), icon=get_icon('dictedit.png'), tip=_("Show/hide global variables explorer"), toggled=self.toggle_globals_explorer, text_beside_icon=True) if self.terminate_button is None: self.terminate_button = create_toolbutton(self, text=_("Terminate"), icon=get_icon('terminate.png'), tip=_("""Attempts to terminate the process. The process may not exit as a result of clicking this button (it is given the chance to prompt the user for any unsaved files, etc).""")) buttons = [] if self.namespacebrowser_button is not None: buttons.append(self.namespacebrowser_button) buttons += [self.run_button, self.options_button, self.terminate_button, self.kill_button] return buttons def get_options_menu(self): ExternalShellBase.get_options_menu(self) self.interact_action = create_action(self, _("Interact")) self.interact_action.setCheckable(True) self.debug_action = create_action(self, _("Debug")) self.debug_action.setCheckable(True) self.args_action = create_action(self, _("Arguments..."), triggered=self.get_arguments) run_settings_menu = QMenu(_("Run settings"), self) add_actions(run_settings_menu, (self.interact_action, self.debug_action, self.args_action)) self.cwd_button = create_action(self, _("Working directory"), icon=get_std_icon('DirOpenIcon'), tip=_("Set current working directory"), triggered=self.set_current_working_directory) self.env_button = create_action(self, _("Environment variables"), icon=get_icon('environ.png'), triggered=self.show_env) self.syspath_button = create_action(self, _("Show sys.path contents"), icon=get_icon('syspath.png'), triggered=self.show_syspath) actions = [run_settings_menu, self.show_time_action, None, self.cwd_button, self.env_button, self.syspath_button] if self.menu_actions is not None: actions += [None]+self.menu_actions return actions def is_interpreter(self): """Return True if shellwidget is a Python interpreter""" return self.is_interpreter def get_shell_widget(self): if self.stand_alone is None: return self.shell else: self.namespacebrowser = NamespaceBrowser(self) settings = self.stand_alone self.namespacebrowser.set_shellwidget(self) self.namespacebrowser.setup(**settings) self.connect(self.namespacebrowser, SIGNAL('collapse()'), lambda: self.toggle_globals_explorer(False)) # Shell splitter self.splitter = splitter = QSplitter(Qt.Vertical, self) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.splitter_moved) splitter.addWidget(self.shell) splitter.setCollapsible(0, False) splitter.addWidget(self.namespacebrowser) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 0) splitter.setHandleWidth(5) splitter.setSizes([2, 1]) return splitter def get_icon(self): return get_icon('python.png') def set_buttons_runnning_state(self, state): ExternalShellBase.set_buttons_runnning_state(self, state) self.interact_action.setEnabled(not state and not self.is_interpreter) self.debug_action.setEnabled(not state and not self.is_interpreter) self.args_action.setEnabled(not state and not self.is_interpreter) if state: if self.arguments: argstr = _("Arguments: %s") % self.arguments else: argstr = _("No argument") else: argstr = _("Arguments...") self.args_action.setText(argstr) self.terminate_button.setVisible(not self.is_interpreter and state) if not state: self.toggle_globals_explorer(False) for btn in (self.cwd_button, self.env_button, self.syspath_button): btn.setEnabled(state and self.monitor_enabled) if self.namespacebrowser_button is not None: self.namespacebrowser_button.setEnabled(state) def set_namespacebrowser(self, namespacebrowser): """ Set namespace browser *widget* Note: this method is not used in stand alone mode """ self.namespacebrowser = namespacebrowser self.configure_namespacebrowser() def configure_namespacebrowser(self): """Connect the namespace browser to the notification thread""" if self.notification_thread is not None: self.connect(self.notification_thread, SIGNAL('refresh_namespace_browser()'), self.namespacebrowser.refresh_table) signal = self.notification_thread.sig_process_remote_view signal.connect(self.namespacebrowser.process_remote_view) def create_process(self): self.shell.clear() self.process = QProcess(self) if self.merge_output_channels: self.process.setProcessChannelMode(QProcess.MergedChannels) else: self.process.setProcessChannelMode(QProcess.SeparateChannels) self.connect(self.shell, SIGNAL("wait_for_ready_read()"), lambda: self.process.waitForReadyRead(250)) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) #-------------------------Python specific------------------------------- # Python arguments p_args = ['-u'] if DEBUG >= 3: p_args += ['-v'] p_args += get_python_args(self.fname, self.python_args, self.interact_action.isChecked(), self.debug_action.isChecked(), self.arguments) env = [unicode(_path) for _path in self.process.systemEnvironment()] if self.pythonstartup: env.append('PYTHONSTARTUP=%s' % self.pythonstartup) # Monitor if self.monitor_enabled: env.append('SPYDER_SHELL_ID=%s' % id(self)) env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout) env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state) from SMlib.widgets.externalshell import introspection introspection_server = introspection.start_introspection_server() introspection_server.register(self) notification_server = introspection.start_notification_server() self.notification_thread = notification_server.register(self) self.connect(self.notification_thread, SIGNAL('pdb(QString,int)'), lambda fname, lineno: self.emit(SIGNAL('pdb(QString,int)'), fname, lineno)) self.connect(self.notification_thread, SIGNAL('new_ipython_kernel(QString)'), lambda args: self.emit(SIGNAL('create_ipython_client(QString)'), args)) self.connect(self.notification_thread, SIGNAL('open_file(QString,int)'), lambda fname, lineno: self.emit(SIGNAL('open_file(QString,int)'), fname, lineno)) if self.namespacebrowser is not None: self.configure_namespacebrowser() env.append('SPYDER_I_PORT=%d' % introspection_server.port) env.append('SPYDER_N_PORT=%d' % notification_server.port) # External modules options env.append('ETS_TOOLKIT=%s' % self.ets_backend) env.append('MATPLOTLIB_PATCH=%r' % self.mpl_patch_enabled) if self.mpl_backend: env.append('MATPLOTLIB_BACKEND=%s' % self.mpl_backend) if self.qt_api: env.append('QT_API=%s' % self.qt_api) env.append('INSTALL_QT_INPUTHOOK=%s' % self.install_qt_inputhook) env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr) # # Socket-based alternative (see input hook in sitecustomize.py): # if self.install_qt_inputhook: # from PyQt4.QtNetwork import QLocalServer # self.local_server = QLocalServer() # self.local_server.listen(str(id(self))) if self.pyqt_api: env.append('PYQT_API=%d' % self.pyqt_api) env.append('IGNORE_SIP_SETAPI_ERRORS=%s' % self.ignore_sip_setapi_errors) # User Module Deleter if self.is_interpreter: env.append('UMD_ENABLED=%r' % self.umd_enabled) env.append('UMD_NAMELIST=%s' % ','.join(self.umd_namelist)) env.append('UMD_VERBOSE=%r' % self.umd_verbose) # IPython kernel env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel) pathlist = [] # Fix encoding with custom "sitecustomize.py" scpath = osp.dirname(osp.abspath(__file__)) pathlist.append(scpath) # Adding Spyder path pathlist += self.path # Adding path list to PYTHONPATH environment variable add_pathlist_to_PYTHONPATH(env, pathlist) #-------------------------Python specific------------------------------- self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("readyReadStandardError()"), self.write_error) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self, SIGNAL('finished()'), self.dialog_manager.close_all) self.connect(self.terminate_button, SIGNAL("clicked()"), self.process.terminate) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) #-------------------------Python specific------------------------------- # Fixes for our Mac app: # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app, # but their values are messing sys.path for external interpreters # (e.g. EPD) so we need to remove them from the environment. # 2. Add this file's dir to PYTHONPATH. This will make every external # interpreter to use our sitecustomize script. # 3. Remove PYTHONOPTIMIZE from env so that we can have assert # statements working with our interpreters (See Issue 1281) if sys.platform == 'darwin' and 'Spyder.app' in __file__: env.append('SPYDER_INTERPRETER=%s' % self.pythonexecutable) if 'Spyder.app' not in self.pythonexecutable: env = [p for p in env if not (p.startswith('PYTHONPATH') or \ p.startswith('PYTHONHOME'))] # 1. env.append('PYTHONPATH=%s' % osp.dirname(__file__)) # 2. env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')] # 3. self.process.setEnvironment(env) self.process.start(self.pythonexecutable, p_args) #-------------------------Python specific------------------------------- running = self.process.waitForStarted(3000) self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("A Python or IPython Console failed to start!")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process def finished(self, exit_code, exit_status): """Reimplement ExternalShellBase method""" ExternalShellBase.finished(self, exit_code, exit_status) self.introspection_socket = None #=============================================================================== # Input/Output #=============================================================================== def write_error(self): if os.name == 'nt': #---This is apparently necessary only on Windows (not sure though): # emptying standard output buffer before writing error output self.process.setReadChannel(QProcess.StandardOutput) if self.process.waitForReadyRead(1): self.write_output() self.shell.write_error(self.get_stderr()) QApplication.processEvents() def send_to_process(self, text): if not self.is_running(): return if not isinstance(text, basestring): text = unicode(text) if self.install_qt_inputhook and self.introspection_socket is not None: communicate(self.introspection_socket, "toggle_inputhook_flag(True)") # # Socket-based alternative (see input hook in sitecustomize.py): # while self.local_server.hasPendingConnections(): # self.local_server.nextPendingConnection().write('go!') if text.startswith(('%', '!')): text = 'evalsc(r"%s")\n' % text if not text.endswith('\n'): text += '\n' self.process.write(locale_codec.fromUnicode(text)) self.process.waitForBytesWritten(-1) # Eventually write prompt faster (when hitting Enter continuously) # -- necessary/working on Windows only: if os.name == 'nt': self.write_error() def keyboard_interrupt(self): if self.introspection_socket is not None: communicate(self.introspection_socket, "thread.interrupt_main()") def quit_monitor(self): if self.introspection_socket is not None: try: write_packet(self.introspection_socket, "thread.exit()") except socket.error: pass #=============================================================================== # Globals explorer #=============================================================================== def toggle_globals_explorer(self, state): if self.stand_alone is not None: self.splitter.setSizes([1, 1 if state else 0]) self.namespacebrowser_button.setChecked(state) if state and self.namespacebrowser is not None: self.namespacebrowser.refresh_table() def splitter_moved(self, pos, index): self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] ) #=============================================================================== # Misc. #=============================================================================== def set_current_working_directory(self): """Set current working directory""" cwd = self.shell.get_cwd() self.emit(SIGNAL('redirect_stdio(bool)'), False) directory = getexistingdirectory(self, _("Select directory"), cwd) if directory: self.shell.set_cwd(directory) self.emit(SIGNAL('redirect_stdio(bool)'), True) def show_env(self): """Show environment variables""" get_func = self.shell.get_env set_func = self.shell.set_env self.dialog_manager.show(RemoteEnvDialog(get_func, set_func)) def show_syspath(self): """Show sys.path contents""" editor = DictEditor() editor.setup(self.shell.get_syspath(), title="sys.path", readonly=True, width=600, icon='syspath.png') self.dialog_manager.show(editor)
class ExternalSystemShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = TerminalWidget def __init__(self, parent=None, wdir=None, path=[], light_background=True, menu_actions=None, show_buttons_inside=True, show_elapsed_time=True): ExternalShellBase.__init__(self, parent=parent, fname=None, wdir=wdir, history_filename='.history', light_background=light_background, menu_actions=menu_actions, show_buttons_inside=show_buttons_inside, show_elapsed_time=show_elapsed_time) # Additional python path list self.path = path # For compatibility with the other shells that can live in the external # console self.is_ipykernel = False self.connection_file = None def get_icon(self): return get_icon('cmdprompt.png') def create_process(self): self.shell.clear() self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe) env = [unicode(_path) for _path in self.process.systemEnvironment()] add_pathlist_to_PYTHONPATH(env, self.path) self.process.setEnvironment(env) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) # Shell arguments if os.name == 'nt': p_args = ['/Q'] else: p_args = ['-i'] if self.arguments: p_args.extend( shell_split(self.arguments) ) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) if os.name == 'nt': self.process.start('cmd.exe', p_args) else: # Using bash: self.process.start('bash', p_args) self.send_to_process("""PS1="\u@\h:\w> "\n""") running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process #=============================================================================== # Input/Output #=============================================================================== def transcode(self, bytes): if os.name == 'nt': return encoding.transcode(str(bytes.data()), 'cp850') else: return ExternalShellBase.transcode(self, bytes) def send_to_process(self, text): if not isinstance(text, basestring): text = unicode(text) if text[:-1] in ["clear", "cls", "CLS"]: self.shell.clear() self.send_to_process(os.linesep) return if not text.endswith('\n'): text += '\n' if os.name == 'nt': self.process.write(text.encode('cp850')) else: self.process.write(locale_codec.fromUnicode(text)) self.process.waitForBytesWritten(-1) def keyboard_interrupt(self): # This does not work on Windows: # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe) self.send_ctrl_to_process('c') # # The following code will soon be removed: # # (last attempt to send a Ctrl+C on Windows) # if os.name == 'nt': # pid = int(self.process.pid()) # import ctypes, win32api, win32con # class _PROCESS_INFORMATION(ctypes.Structure): # _fields_ = [("hProcess", ctypes.c_int), # ("hThread", ctypes.c_int), # ("dwProcessID", ctypes.c_int), # ("dwThreadID", ctypes.c_int)] # x = ctypes.cast( ctypes.c_void_p(pid), # ctypes.POINTER(_PROCESS_INFORMATION) ) # win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, # x.dwProcessID) # else: # self.send_ctrl_to_process('c')
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) self.ui = Ui_HostWindow() self.ui.setupUi(self) # ---------------------------------------------------------------------------------------------------- # Internal stuff # Current mod-ui title #self.fCurrentBundle = "" self.fCurrentTitle = "" # Next bundle to load (done by startup arguments) self.fNextBundle = "" # first attempt of auto-start backend doesn't show an error self.fFirstBackendInit = True # need to call session reconnect after connecting the 1st time self.fNeedsSessionReconnect = False # Qt idle timer self.fIdleTimerId = 0 # Qt web frame, used for evaluating javascript self.fWebFrame = None # to be filled with key-value pairs of current settings self.fSavedSettings = {} # List of pedalboards self.fPedalboards = get_all_pedalboards() # List of current-pedalboard presets self.fPresetMenuList = [] # Process that runs the backend self.fProccessBackend = QProcess(self) self.fProccessBackend.setProcessChannelMode(QProcess.MergedChannels) self.fProccessBackend.setReadChannel(QProcess.StandardOutput) self.fStoppingBackend = False # Thread for managing the webserver self.fWebServerThread = WebServerThread(self) # ---------------------------------------------------------------------------------------------------- # Set up GUI self.ui.webview = QWebView(self.ui.swp_webview) self.ui.webview.setMinimumWidth(980) self.ui.swp_webview.layout().addWidget(self.ui.webview) self.ui.webpage = HostWebPage(self) self.ui.webpage.setViewportSize(QSize(980, 600)) self.ui.webview.setPage(self.ui.webpage) self.ui.webinspector = QWebInspector(None) self.ui.webinspector.resize(800, 600) self.ui.webinspector.setPage(self.ui.webpage) self.ui.webinspector.setVisible(False) self.ui.act_file_connect.setEnabled(False) self.ui.act_file_connect.setVisible(False) self.ui.act_file_disconnect.setEnabled(False) self.ui.act_file_disconnect.setVisible(False) self.ui.label_app.setText("MOD Application v%s" % config["version"]) # disable file menu self.ui.act_file_refresh.setEnabled(False) self.ui.act_file_inspect.setEnabled(False) # disable pedalboard menu self.ui.act_pedalboard_new.setEnabled(False) self.ui.act_pedalboard_open.setEnabled(False) self.ui.act_pedalboard_save.setEnabled(False) self.ui.act_pedalboard_save_as.setEnabled(False) self.ui.act_pedalboard_share.setEnabled(False) self.ui.menu_Pedalboard.setEnabled(False) # disable presets menu self.ui.act_presets_new.setEnabled(False) self.ui.act_presets_save.setEnabled(False) self.ui.act_presets_save_as.setEnabled(False) self.ui.menu_Presets.setEnabled(False) # initial stopped state self.slot_backendFinished(-1, -1) # Qt needs this so it properly creates & resizes the webview self.ui.stackedwidget.setCurrentIndex(1) self.ui.stackedwidget.setCurrentIndex(0) # FIXME #self.ui.act_backend_stop.setVisible(False) #self.ui.act_backend_restart.setVisible(False) # ---------------------------------------------------------------------------------------------------- # Set up GUI (special stuff for Mac OS) if MACOS: self.ui.act_file_quit.setMenuRole(QAction.QuitRole) self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole) self.ui.act_help_about.setMenuRole(QAction.AboutRole) #self.ui.menu_Settings.setTitle("Panels") #self.ui.menu_Help.hide() # ---------------------------------------------------------------------------------------------------- # Load Settings self.loadSettings(True) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGUSR1.connect(self.slot_handleSIGUSR1) self.SIGTERM.connect(self.slot_handleSIGTERM) self.fProccessBackend.error.connect(self.slot_backendError) self.fProccessBackend.started.connect(self.slot_backendStarted) self.fProccessBackend.finished.connect(self.slot_backendFinished) self.fProccessBackend.readyRead.connect(self.slot_backendRead) self.fWebServerThread.running.connect(self.slot_webServerRunning) self.fWebServerThread.finished.connect(self.slot_webServerFinished) self.ui.act_file_refresh.triggered.connect(self.slot_fileRefresh) self.ui.act_file_inspect.triggered.connect(self.slot_fileInspect) self.ui.act_settings_configure.triggered.connect(self.slot_configure) self.ui.act_help_about.triggered.connect(self.slot_about) self.ui.act_help_project.triggered.connect(self.slot_showProject) self.ui.act_help_website.triggered.connect(self.slot_showWebsite) self.ui.b_start.clicked.connect(self.slot_backendStart) self.ui.b_configure.clicked.connect(self.slot_configure) self.ui.b_about.clicked.connect(self.slot_about) # force our custom refresh webReloadAction = self.ui.webpage.action(QWebPage.Reload) webReloadAction.triggered.disconnect() webReloadAction.triggered.connect(self.slot_fileRefresh) # ---------------------------------------------------------------------------------------------------- # Final setup self.setProperWindowTitle() SESSION.setupApp(self._pedal_changed_callback) if not "--no-autostart" in sys.argv: QTimer.singleShot(0, self.slot_backendStart) QTimer.singleShot(1, self.fixWebViewSize) def __del__(self): self.stopAndWaitForWebServer() self.stopAndWaitForBackend() def _pedal_changed_callback(self, ok, bundlepath, title): #self.fCurrentBundle = bundlepath self.fCurrentTitle = title or "" #self.updatePresetsMenu() self.setProperWindowTitle() # -------------------------------------------------------------------------------------------------------- # Files (menu actions) @pyqtSlot() def slot_fileRefresh(self): if self.fWebFrame is None: return self.ui.label_progress.setText(self.tr("Refreshing UI...")) self.ui.stackedwidget.setCurrentIndex(0) QTimer.singleShot(0, self.ui.webview.reload) @pyqtSlot() def slot_fileInspect(self): self.ui.webinspector.show() # -------------------------------------------------------------------------------------------------------- # Settings (menu actions) @pyqtSlot() def slot_configure(self): dialog = SettingsWindow(self, True) if not dialog.exec_(): return self.loadSettings(False) # -------------------------------------------------------------------------------------------------------- # About (menu actions) @pyqtSlot() def slot_about(self): QMessageBox.about( self, self.tr("About"), self.tr(""" <b>MOD Desktop Application</b><br/> <br/> A software to have the complete MOD environment running in your desktop.<br/> (C) 2015-2016 - The MOD Team<br/> <br/> Publications, products, content or services referenced herein or on the website are the exclusive trademarks or servicemarks of MOD.<br/> Other product and company names mentioned in the site may be the trademarks of their respective owners.<br/> <br/> All software is available under the <a href="https://www.gnu.org/licenses/gpl-2.0.html">GPL license</a>.<br/> """)) @pyqtSlot() def slot_showProject(self): QDesktopServices.openUrl(QUrl("https://github.com/moddevices/mod-app")) @pyqtSlot() def slot_showWebsite(self): QDesktopServices.openUrl(QUrl("http://moddevices.com/")) # -------------------------------------------------------------------------------------------------------- # Backend (menu actions) @pyqtSlot() def slot_backendInformation(self): table = """ <table><tr> <td> MOD-UI port: <td></td> %s </td> </tr></table> """ % (config["port"], ) QMessageBox.information(self, self.tr("information"), table) @pyqtSlot() def slot_backendStart(self): if self.fProccessBackend.state() != QProcess.NotRunning: print("slot_backendStart ignored") return print("slot_backendStart in progress...") hostPath = self.fSavedSettings[MOD_KEY_HOST_PATH] if hostPath.endswith("ingen"): hostPath = MOD_DEFAULT_HOST_PATH hostArgs = ["-p", "5555", "-f", "5556"] if self.fSavedSettings[MOD_KEY_HOST_VERBOSE]: hostArgs.append("-v") else: hostArgs.append("-n") self.fProccessBackend.start(hostPath, hostArgs) @pyqtSlot() def slot_backendStop(self): #if self.fPluginCount > 0: #if not forced: #ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" #"Do you want to do this now?"), #QMessageBox.Yes | QMessageBox.No, QMessageBox.No) #if ask != QMessageBox.Yes: #return #self.removeAllPlugins() #self.host.set_engine_about_to_close() #self.host.remove_all_plugins() # testing red color for server stopped self.ui.webview.blockSignals(True) self.ui.webview.setHtml("<html><body bgcolor='green'></body></html>") self.ui.webview.blockSignals(False) self.stopAndWaitForWebServer() self.stopAndWaitForBackend() @pyqtSlot() def slot_backendRestart(self): self.slot_backendStop() self.slot_backendStart() # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_backendStarted(self): self.ui.act_backend_start.setEnabled(False) self.ui.act_backend_stop.setEnabled(True) self.ui.act_backend_restart.setEnabled(True) self.ui.w_buttons.setEnabled(False) self.ui.label_progress.setText(self.tr("Loading backend...")) @pyqtSlot(int, QProcess.ExitStatus) def slot_backendFinished(self, exitCode, exitStatus): self.fFirstBackendInit = False self.fStoppingBackend = False self.ui.act_backend_start.setEnabled(True) self.ui.act_backend_stop.setEnabled(False) self.ui.act_backend_restart.setEnabled(False) self.ui.w_buttons.setEnabled(True) self.ui.label_progress.setText("") self.ui.stackedwidget.setCurrentIndex(0) # stop webserver self.stopAndWaitForWebServer() @pyqtSlot(QProcess.ProcessError) def slot_backendError(self, error): firstBackendInit = self.fFirstBackendInit self.fFirstBackendInit = False # stop webserver self.stopAndWaitForWebServer() # crashed while stopping, ignore if error == QProcess.Crashed and self.fStoppingBackend: return errorStr = self.tr("Could not start host backend.\n" ) + self.getProcessErrorAsString(error) qWarning(errorStr) # don't show error if this is the first time starting the host or using live-iso if firstBackendInit: return # show the error message QMessageBox.critical(self, self.tr("Error"), errorStr) @pyqtSlot() def slot_backendRead(self): #if self.fProccessBackend.state() != QProcess.Running: #return for line in str( self.fProccessBackend.readAllStandardOutput().trimmed(), encoding="utf-8", errors="ignore").strip().split("\n"): line = line.replace("\x1b[0m", "").replace("\x1b[0;31m", "").replace("\x1b[0;33m", "").strip() if not line: continue if self.fSavedSettings[MOD_KEY_HOST_VERBOSE]: print("BACKEND:", line) if line == "mod-host ready!" or line == "mod-host is running.": QTimer.singleShot(0, self.slot_backendStartPhase2) #elif "Listening on socket " in line: #QTimer.singleShot(1000, self.slot_ingenStarted) ##elif "Activated Jack client " in line: ##QTimer.singleShot(1000, self.fWebServerThread.start) #elif "Failed to create UNIX socket" in line or "Could not activate Jack client" in line: ## need to wait for ingen to create sockets so it can delete them on termination #QTimer.singleShot(1000, self.slot_ingenStartError) @pyqtSlot() def slot_backendStartPhase2(self): if self.fProccessBackend.state() == QProcess.NotRunning: return if not self.fNeedsSessionReconnect: # we'll need it for next time self.fNeedsSessionReconnect = True else: # we need it now SESSION.reconnectApp() self.fWebServerThread.start() @pyqtSlot() def slot_backendStartError(self): self.stopAndWaitForBackend() self.slot_backendError(-2) # -------------------------------------------------------------------------------------------------------- # Web Server @pyqtSlot() def slot_webServerRunning(self): self.ui.webview.loadStarted.connect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.connect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.connect(self.slot_webviewLoadFinished) print("webserver running with URL:", config["addr"]) self.ui.webview.load(QUrl(config["addr"])) @pyqtSlot() def slot_webServerFinished(self): try: self.ui.webview.loadStarted.disconnect( self.slot_webviewLoadStarted) self.ui.webview.loadProgress.disconnect( self.slot_webviewLoadProgress) self.ui.webview.loadFinished.disconnect( self.slot_webviewLoadFinished) except: pass print("webserver finished") # testing red color for server finished self.ui.webview.blockSignals(True) self.ui.webview.setHtml("<html><body bgcolor='red'></body></html>") self.ui.webview.blockSignals(False) # -------------------------------------------------------------------------------------------------------- # Web View @pyqtSlot() def slot_webviewLoadStarted(self): self.ui.label_progress.setText(self.tr("Loading UI...")) print("load started") @pyqtSlot(int) def slot_webviewLoadProgress(self, progress): self.ui.label_progress.setText(self.tr("Loading UI... %i%%" % progress)) @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): self.ui.webview.loadStarted.disconnect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.disconnect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.disconnect(self.slot_webviewLoadFinished) if ok: # message self.ui.label_progress.setText(self.tr("Loading UI... finished!")) # enable file menu self.ui.act_file_refresh.setEnabled(True) self.ui.act_file_inspect.setEnabled(True) # for js evaulation self.fWebFrame = self.ui.webpage.currentFrame() # postpone app stuff QTimer.singleShot(100, self.slot_webviewPostFinished) else: # message self.ui.label_progress.setText(self.tr("Loading UI... failed!")) # disable file menu self.ui.act_file_refresh.setEnabled(False) self.ui.act_file_inspect.setEnabled(False) # disable pedalboard menu self.ui.act_pedalboard_new.setEnabled(False) self.ui.act_pedalboard_open.setEnabled(False) self.ui.act_pedalboard_save.setEnabled(False) self.ui.act_pedalboard_save_as.setEnabled(False) self.ui.act_pedalboard_share.setEnabled(False) self.ui.menu_Pedalboard.setEnabled(False) # stop js evaulation self.fWebFrame = None # stop backend&server self.stopAndWaitForWebServer() self.stopAndWaitForBackend() print("load finished") @pyqtSlot() def slot_webviewPostFinished(self): if self.fNextBundle: bundle = self.fNextBundle self.fNextBundle = "" self.fWebFrame.evaluateJavaScript( "desktop.loadPedalboard(\"%s\")" % bundle) QTimer.singleShot(0, self.slot_webviewPostFinished2) @pyqtSlot() def slot_webviewPostFinished2(self): self.ui.stackedwidget.setCurrentIndex(1) # -------------------------------------------------------------------------------------------------------- # Settings def saveSettings(self): settings = QSettings() settings.setValue("Geometry", self.saveGeometry()) def loadSettings(self, firstTime): qsettings = QSettings() websettings = self.ui.webview.settings() self.fSavedSettings = { # Main MOD_KEY_MAIN_PROJECT_FOLDER: qsettings.value(MOD_KEY_MAIN_PROJECT_FOLDER, MOD_DEFAULT_MAIN_PROJECT_FOLDER, type=str), MOD_KEY_MAIN_REFRESH_INTERVAL: qsettings.value(MOD_KEY_MAIN_REFRESH_INTERVAL, MOD_DEFAULT_MAIN_REFRESH_INTERVAL, type=int), # Host MOD_KEY_HOST_VERBOSE: qsettings.value(MOD_KEY_HOST_VERBOSE, MOD_DEFAULT_HOST_VERBOSE, type=bool), MOD_KEY_HOST_PATH: qsettings.value(MOD_KEY_HOST_PATH, MOD_DEFAULT_HOST_PATH, type=str), # WebView MOD_KEY_WEBVIEW_INSPECTOR: qsettings.value(MOD_KEY_WEBVIEW_INSPECTOR, MOD_DEFAULT_WEBVIEW_INSPECTOR, type=bool), MOD_KEY_WEBVIEW_VERBOSE: qsettings.value(MOD_KEY_WEBVIEW_VERBOSE, MOD_DEFAULT_WEBVIEW_VERBOSE, type=bool), MOD_KEY_WEBVIEW_SHOW_INSPECTOR: qsettings.value(MOD_KEY_WEBVIEW_SHOW_INSPECTOR, MOD_DEFAULT_WEBVIEW_SHOW_INSPECTOR, type=bool) } inspectorEnabled = self.fSavedSettings[MOD_KEY_WEBVIEW_INSPECTOR] websettings.setAttribute(QWebSettings.DeveloperExtrasEnabled, inspectorEnabled) if firstTime: if qsettings.contains("Geometry"): self.restoreGeometry(qsettings.value("Geometry", "")) else: self.setWindowState(self.windowState() | Qt.WindowMaximized) if inspectorEnabled and self.fSavedSettings[ MOD_KEY_WEBVIEW_SHOW_INSPECTOR]: QTimer.singleShot(1000, self.ui.webinspector.show) self.ui.act_file_inspect.setVisible(inspectorEnabled) if self.fIdleTimerId != 0: self.killTimer(self.fIdleTimerId) self.fIdleTimerId = self.startTimer( self.fSavedSettings[MOD_KEY_MAIN_REFRESH_INTERVAL]) # -------------------------------------------------------------------------------------------------------- # Misc @pyqtSlot() def slot_handleSIGUSR1(self): print("Got SIGUSR1 -> Saving project now") self.slot_pedalboardSave() @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): if self.fIdleTimerId != 0: self.killTimer(self.fIdleTimerId) self.fIdleTimerId = 0 self.saveSettings() self.slot_backendStop() QMainWindow.closeEvent(self, event) # Needed in case the web inspector is still alive #self.ui.webinspector.close() QApplication.instance().quit() def timerEvent(self, event): if event.timerId() == self.fIdleTimerId: pass QMainWindow.timerEvent(self, event) def resizeEvent(self, event): QMainWindow.resizeEvent(self, event) self.fixWebViewSize() # -------------------------------------------------------------------------------------------------------- # Internal stuff def getProcessErrorAsString(self, error): if error == -2: return self.tr("Ingen failed to create UNIX socket.") if error == QProcess.FailedToStart: return self.tr("Process failed to start.") if error == QProcess.Crashed: return self.tr("Process crashed.") if error == QProcess.Timedout: return self.tr("Process timed out.") if error == QProcess.WriteError: return self.tr("Process write error.") return self.tr("Unkown error.") def fixWebViewSize(self): if self.ui.stackedwidget.currentIndex() == 1: return size = self.ui.swp_intro.size() self.ui.swp_webview.resize(size) self.ui.webview.resize(size) self.ui.webpage.setViewportSize(size) def stopAndWaitForBackend(self): SESSION.host.close_jack() if self.fProccessBackend.state() == QProcess.NotRunning: return self.fStoppingBackend = True self.fProccessBackend.terminate() if not self.fProccessBackend.waitForFinished(2000): qWarning("Backend failed top stop cleanly, forced kill") self.fProccessBackend.kill() def stopAndWaitForWebServer(self): if not self.fWebServerThread.isRunning(): return if not self.fWebServerThread.stopWait(): qWarning( "WebServer Thread failed top stop cleanly, forced terminate") self.fWebServerThread.terminate() def setProperWindowTitle(self): title = "MOD Application" if self.fCurrentTitle: title += " - %s" % self.fCurrentTitle self.setWindowTitle(title)
class ExternalPythonShell(ExternalShellBase): """External Shell widget: execute Python script in a separate process""" SHELL_CLASS = ExtPyQsciShell def __init__(self, parent=None, fname=None, wdir=None, commands=[], interact=False, debug=False, path=[]): ExternalShellBase.__init__(self, parent, wdir, history_filename='.history_ec.py') self.shell.set_externalshell(self) self.toggle_globals_explorer(False) self.interact_check.setChecked(interact) self.debug_check.setChecked(debug) self.monitor_socket = None self.interpreter = fname is None self.fname = startup.__file__ if fname is None else fname if self.interpreter: self.interact_check.hide() self.debug_check.hide() self.terminate_button.hide() self.commands = ["import sys", "sys.path.insert(0, '')"] + commands # Additional python path list self.path = path def get_toolbar_buttons(self): ExternalShellBase.get_toolbar_buttons(self) self.globalsexplorer_button = create_toolbutton(self, get_icon('dictedit.png'), self.tr("Variables"), tip=self.tr("Show/hide global variables explorer"), toggled=self.toggle_globals_explorer) self.terminate_button = create_toolbutton(self, get_icon('terminate.png'), self.tr("Terminate"), tip=self.tr("Attempts to terminate the process.\n" "The process may not exit as a result of clicking " "this button\n(it is given the chance to prompt " "the user for any unsaved files, etc).")) self.interact_check = QCheckBox(self.tr("Interact"), self) self.debug_check = QCheckBox(self.tr("Debug"), self) return [self.interact_check, self.debug_check, self.globalsexplorer_button, self.run_button, self.terminate_button, self.kill_button] def get_shell_widget(self): # Globals explorer self.globalsexplorer = GlobalsExplorer(self) self.connect(self.globalsexplorer, SIGNAL('collapse()'), lambda: self.toggle_globals_explorer(False)) # Shell splitter self.splitter = splitter = QSplitter(Qt.Vertical, self) self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'), self.splitter_moved) splitter.addWidget(self.shell) splitter.setCollapsible(0, False) splitter.addWidget(self.globalsexplorer) splitter.setStretchFactor(0, 2) splitter.setStretchFactor(1, 1) return splitter def get_icon(self): return get_icon('python.png') def set_buttons_runnning_state(self, state): ExternalShellBase.set_buttons_runnning_state(self, state) self.interact_check.setEnabled(not state) self.debug_check.setEnabled(not state) self.terminate_button.setEnabled(state) if not state: self.toggle_globals_explorer(False) self.globalsexplorer_button.setEnabled(state) def create_process(self): self.shell.clear() self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) self.connect(self.shell, SIGNAL("wait_for_ready_read()"), lambda: self.process.waitForReadyRead(250)) # Working directory if self.wdir is not None: self.process.setWorkingDirectory(self.wdir) #-------------------------Python specific------------------------------- # Python arguments p_args = ['-u'] if self.interact_check.isChecked(): p_args.append('-i') if self.debug_check.isChecked(): p_args.extend(['-m', 'pdb']) p_args.append(self.fname) env = self.process.systemEnvironment() # Monitor env.append('SHELL_ID=%s' % id(self)) from spyderlib.widgets.externalshell.monitor import start_server server, port = start_server() self.notification_thread = server.register(str(id(self)), self) self.connect(self.notification_thread, SIGNAL('refresh()'), self.globalsexplorer.refresh_table) env.append('SPYDER_PORT=%d' % port) # Python init commands (interpreter only) if self.commands and self.interpreter: env.append('PYTHONINITCOMMANDS=%s' % ';'.join(self.commands)) self.process.setEnvironment(env) pathlist = [] # Fix encoding with custom "sitecustomize.py" scpath = osp.dirname(osp.abspath(__file__)) pathlist.append(scpath) # Adding Spyder path pathlist += self.path # Adding path list to PYTHONPATH environment variable pypath = "PYTHONPATH" pathstr = os.pathsep.join(pathlist) if os.environ.get(pypath) is not None: env.replaceInStrings(pypath+'=', pypath+'='+pathstr+os.pathsep, Qt.CaseSensitive) else: env.append(pypath+'='+pathstr) self.process.setEnvironment(env) #-------------------------Python specific------------------------------- if self.arguments: p_args.extend( self.arguments.split(' ') ) self.connect(self.process, SIGNAL("readyReadStandardOutput()"), self.write_output) self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"), self.finished) self.connect(self.terminate_button, SIGNAL("clicked()"), self.process.terminate) self.connect(self.kill_button, SIGNAL("clicked()"), self.process.kill) #-------------------------Python specific------------------------------- self.process.start(sys.executable, p_args) #-------------------------Python specific------------------------------- running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, self.tr("Error"), self.tr("Process failed to start")) else: self.shell.setFocus() self.emit(SIGNAL('started()')) return self.process #=============================================================================== # Input/Output #=============================================================================== def _write_error(self, text, findstr): pos = text.find(findstr) if pos != -1: self.shell.write(text[:pos]) if text.endswith(">>> "): self.shell.write_error(text[pos:-5]) self.shell.write(text[-5:], flush=True) else: self.shell.write_error(text[pos:]) return True return False def write_output(self): text = self.get_stdout() if not self._write_error(text, 'Traceback (most recent call last):') \ and not self._write_error(text, 'File "<stdin>", line 1'): self.shell.write(text) QApplication.processEvents() def send_to_process(self, qstr): if not isinstance(qstr, QString): qstr = QString(qstr) if not qstr.endsWith('\n'): qstr.append('\n') self.process.write(qstr.toLocal8Bit()) self.process.waitForBytesWritten(-1) def keyboard_interrupt(self): communicate(self.monitor_socket, "thread.interrupt_main()") #=============================================================================== # Globals explorer #=============================================================================== def toggle_globals_explorer(self, state): self.splitter.setSizes([1, 1 if state else 0]) self.globalsexplorer_button.setChecked(state) if state: self.globalsexplorer.refresh_table() def splitter_moved(self, pos, index): self.globalsexplorer_button.setChecked( self.splitter.sizes()[1] )
class PyLint(Plugin, QObject): """ PyLint Plugin """ capabilities = ['toolbarHook'] __version__ = '0.5' thread = None def do_toolbarHook(self, widget): """ Hook to install the pylint toolbar button""" widget.addAction('PyLint', self.do_pylint) @pyqtSlot() def do_pylint(self): """ Launch the lint process and create the result window """ print 'do_pylint' self.pylint_pross = QProcess() self.pylint_pross.setProcessChannelMode(QProcess.MergedChannels) self.pylint_pross.setWorkingDirectory( \ os.path.dirname(str(self.parent.editor.filename))) self.pylint_pross.setReadChannel(QProcess.StandardOutput) self.connect(self.pylint_pross, \ SIGNAL('finished(int)'), \ self.finished) self.connect(self.pylint_pross, \ SIGNAL('readyReadStandardOutput()'), \ self.handle_stdout) self.connect(self.pylint_pross, \ SIGNAL('readyReadStandardError()'), \ self.handle_stderr) if (self.pylint_pross.start("pylint", \ [self.parent.editor.filename,])): print 'Cannot start process' self.win = ResultWin() self.win.setWindowTitle("PyLint Results :" \ + os.path.basename(str(self.parent.editor.filename))) self.win.show() if isMAEMO: self.win.setAttribute(Qt.WA_Maemo5ShowProgressIndicator, True) self.win.connect(self.win.list_view, \ SIGNAL('doubleClicked(const QModelIndex&)'), \ self.goto_line) self.pylint_pross.waitForStarted() def finished(self, _): """ Call back called when lint end """ if isMAEMO: self.win.setAttribute(Qt.WA_Maemo5ShowProgressIndicator, False) def handle_stdout(self): """ Private slot to handle the readyReadStdout signal of the pylint process. """ result_list = [] #regex = QRegExp('(\w)\S*:\S*(\d*):.*: (.*)') regex = QRegExp('(\w)\s*:\s*(\d*):(.*)') regex_score = \ QRegExp('.*at.(\d.\d*)/10.*') while self.pylint_pross and self.pylint_pross.canReadLine(): result = unicode(self.pylint_pross.readLine()) if result != None: pos = 0 while True: pos = regex.indexIn(result, pos) if pos < 0: if regex_score.indexIn(result, 0) >= 0: self.win.setWindowTitle( \ "PyLint Results :" \ + str(regex_score.cap(1)) \ + ':' + os.path.basename(str(self.parent.editor.filename))) break result_list.append((regex.cap(1), regex.cap(2), regex.cap(3))) #print 'Append : ',(regex.cap(1), regex.cap(2), regex.cap(3)) pos = pos + regex.matchedLength() if len(result_list)>0: self.win.append_results(result_list) def goto_line(self, index): """ Callback called when a lint result is double clicked """ line = int(self.win.list_model.items[index.row()][1]) self.parent.do_gotoLine(line) def handle_stderr(self): """ Private slot to handle the readyReadStderr signal of the pylint process. Currently not managed """ print 'error stderr'
class PyLint(Plugin, QObject): """ PyLint Plugin """ capabilities = ['toolbarHook'] __version__ = '0.5' thread = None def do_toolbarHook(self, widget): """ Hook to install the pylint toolbar button""" widget.addAction('PyLint', self.do_pylint) @pyqtSlot() def do_pylint(self): """ Launch the lint process and create the result window """ print 'do_pylint' self.pylint_pross = QProcess() self.pylint_pross.setProcessChannelMode(QProcess.MergedChannels) self.pylint_pross.setWorkingDirectory( \ os.path.dirname(str(self.parent.editor.filename))) self.pylint_pross.setReadChannel(QProcess.StandardOutput) self.connect(self.pylint_pross, \ SIGNAL('finished(int)'), \ self.finished) self.connect(self.pylint_pross, \ SIGNAL('readyReadStandardOutput()'), \ self.handle_stdout) self.connect(self.pylint_pross, \ SIGNAL('readyReadStandardError()'), \ self.handle_stderr) if (self.pylint_pross.start("pylint", \ [self.parent.editor.filename,])): print 'Cannot start process' self.win = ResultWin() self.win.setWindowTitle("PyLint Results :" \ + os.path.basename(str(self.parent.editor.filename))) self.win.show() if isMAEMO: self.win.setAttribute(Qt.WA_Maemo5ShowProgressIndicator, True) self.win.connect(self.win.list_view, \ SIGNAL('doubleClicked(const QModelIndex&)'), \ self.goto_line) self.pylint_pross.waitForStarted() def finished(self, _): """ Call back called when lint end """ if isMAEMO: self.win.setAttribute(Qt.WA_Maemo5ShowProgressIndicator, False) def handle_stdout(self): """ Private slot to handle the readyReadStdout signal of the pylint process. """ result_list = [] #regex = QRegExp('(\w)\S*:\S*(\d*):.*: (.*)') regex = QRegExp('(\w)\s*:\s*(\d*):(.*)') regex_score = \ QRegExp('.*at.(\d.\d*)/10.*') while self.pylint_pross and self.pylint_pross.canReadLine(): result = unicode(self.pylint_pross.readLine()) if result != None: pos = 0 while True: pos = regex.indexIn(result, pos) if pos < 0: if regex_score.indexIn(result, 0) >= 0: self.win.setWindowTitle( \ "PyLint Results :" \ + str(regex_score.cap(1)) \ + ':' + os.path.basename(str(self.parent.editor.filename))) break result_list.append( (regex.cap(1), regex.cap(2), regex.cap(3))) #print 'Append : ',(regex.cap(1), regex.cap(2), regex.cap(3)) pos = pos + regex.matchedLength() if len(result_list) > 0: self.win.append_results(result_list) def goto_line(self, index): """ Callback called when a lint result is double clicked """ line = int(self.win.list_model.items[index.row()][1]) self.parent.do_gotoLine(line) def handle_stderr(self): """ Private slot to handle the readyReadStderr signal of the pylint process. Currently not managed """ print 'error stderr'
class ScriptRunner(QObject): def __init__(self, caller, parent=None, env=None, addEnv=None): QObject.__init__(self, parent) self._proc = None self._caller = caller self._decode = None if env is None: env = os.environ.copy() self._env = env if addEnv: self._env.update(addEnv) def stop(self, kill=False): if self._proc is None: return if kill: self._proc.kill() else: self._proc.terminate() def run(self, scriptFile, args=None, env=None, wd=None): if self._proc: return self._proc = QProcess(self) if wd: self._proc.setWorkingDirectory(wd) if not env is None: self._env.update(env) envList = dict2qlst(self._env) if args is None: args = [] script = quote(scriptFile) if not os.path.exists(script): script = find_script(script) if not script or not os.path.exists(script): raise PublishException("Script '%s' cannot be found" % script) self._caller.handleScriptOutput("%s %s" % (scriptFile, ' '.join(args))) self._proc.setEnvironment(envList) self._proc.setProcessChannelMode(QProcess.MergedChannels) QObject.connect(self._proc, QtCore.SIGNAL("readyReadStandardOutput()"), self.readScriptOutput) QObject.connect(self._proc, QtCore.SIGNAL("finished(int, " \ "QProcess::ExitStatus)"), self.scriptFinished) self._proc.start(script, args, QIODevice.ReadOnly) def scriptFinished(self, exitCode, exitStatus): if self._caller: self.readScriptOutput() self._caller.scriptFinished(exitCode, exitStatus == QProcess.Crashed) self._proc = None def readScriptOutput(self): if self._proc is None: return qba = self._proc.readAllStandardOutput() if self._caller and qba.length(): if not self._decode: lang, deflocale = locale.getdefaultlocale() self._decode = codecs.getdecoder(deflocale) data, datalen = self._decode(qba.data()) self._caller.handleScriptOutput(data)
# -*- coding: utf-8 -*- import os from PyQt4.QtGui import * from PyQt4 import QtCore from PyQt4.QtCore import QProcess """ - Processes are defined in the Global scope because I noticed - I also use an interleave stdout and stderr (MergedChannels). we need to do that because CMVS write some output in stderr (?). So, a proper error handling in a separate channel is impossible. """ procB = QProcess() procB.setProcessChannelMode(QProcess.MergedChannels) procC = QProcess() procC.setProcessChannelMode(QProcess.MergedChannels) procP = QProcess() procP.setProcessChannelMode(QProcess.MergedChannels) procCam = QProcess() procCam.setProcessChannelMode(QProcess.MergedChannels) class PPTGUI(QWidget): """The main Widget. TODO: PEP8 TODO: Translations TODO: One Console, One process """
def __createProgramEntry(self, description, exe, versionCommand = "", versionStartsWith = "", versionPosition = 0, version = "", versionCleanup = None, versionRe = None): """ Private method to generate a program entry. @param description descriptive text (string or QString) @param exe name of the executable program (string) @param versionCommand command line switch to get the version info (string) if this is empty, the given version will be shown. @param versionStartsWith start of line identifying version info (string) @param versionPosition index of part containing the version info (integer) @keyparam version version string to show (string) @keyparam versionCleanup tuple of two integers giving string positions start and stop for the version string (tuple of integers) @keyparam versionRe regexp to determine the line identifying version info (string). Takes precedence over versionStartsWith. @return version string of detected or given version (string) """ itm = QTreeWidgetItem(self.programsList, QStringList(description)) font = itm.font(0) font.setBold(True) itm.setFont(0, font) if not exe: itm.setText(1, self.trUtf8("(not configured)")) else: if os.path.isabs(exe): if not Utilities.isExecutable(exe): exe = "" else: exe = Utilities.getExecutablePath(exe) if exe: if versionCommand and \ (versionStartsWith != "" or \ (versionRe is not None and versionRe != "")) and \ versionPosition: proc = QProcess() proc.setProcessChannelMode(QProcess.MergedChannels) proc.start(exe, QStringList(versionCommand)) finished = proc.waitForFinished(10000) if finished: output = \ unicode(proc.readAllStandardOutput(), str(Preferences.getSystem("IOEncoding")), 'replace') if versionRe is None: versionRe = "^%s" % re.escape(versionStartsWith) versionRe = re.compile(versionRe, re.UNICODE) for line in output.splitlines(): if versionRe.search(line): try: version = line.split()[versionPosition] if versionCleanup: version = \ version[versionCleanup[0]:versionCleanup[1]] break except IndexError: version = self.trUtf8("(unknown)") else: version = self.trUtf8("(not executable)") itm2 = QTreeWidgetItem(itm, QStringList() << exe << version) itm.setExpanded(True) else: itm.setText(1, self.trUtf8("(not found)")) QApplication.processEvents() self.programsList.header().resizeSections(QHeaderView.ResizeToContents) self.programsList.header().setStretchLastSection(True) return version
class HostWindow(QMainWindow): # signals SIGTERM = pyqtSignal() SIGUSR1 = pyqtSignal() # -------------------------------------------------------------------------------------------------------- def __init__(self): QMainWindow.__init__(self) self.ui = Ui_HostWindow() self.ui.setupUi(self) # ---------------------------------------------------------------------------------------------------- # Internal stuff # Current mod-ui title #self.fCurrentBundle = "" self.fCurrentTitle = "" # Next bundle to load (done by startup arguments) self.fNextBundle = "" # first attempt of auto-start backend doesn't show an error self.fFirstBackendInit = True # need to call session reconnect after connecting the 1st time self.fNeedsSessionReconnect = False # Qt idle timer self.fIdleTimerId = 0 # Qt web frame, used for evaluating javascript self.fWebFrame = None # to be filled with key-value pairs of current settings self.fSavedSettings = {} # List of pedalboards self.fPedalboards = get_all_pedalboards() # List of current-pedalboard presets self.fPresetMenuList = [] # Process that runs the backend self.fProccessBackend = QProcess(self) self.fProccessBackend.setProcessChannelMode(QProcess.MergedChannels) self.fProccessBackend.setReadChannel(QProcess.StandardOutput) self.fStoppingBackend = False # Thread for managing the webserver self.fWebServerThread = WebServerThread(self) # ---------------------------------------------------------------------------------------------------- # Set up GUI self.ui.webview = QWebView(self.ui.swp_webview) self.ui.webview.setMinimumWidth(980) self.ui.swp_webview.layout().addWidget(self.ui.webview) self.ui.webpage = HostWebPage(self) self.ui.webpage.setViewportSize(QSize(980, 600)) self.ui.webview.setPage(self.ui.webpage) self.ui.webinspector = QWebInspector(None) self.ui.webinspector.resize(800, 600) self.ui.webinspector.setPage(self.ui.webpage) self.ui.webinspector.setVisible(False) self.ui.act_file_connect.setEnabled(False) self.ui.act_file_connect.setVisible(False) self.ui.act_file_disconnect.setEnabled(False) self.ui.act_file_disconnect.setVisible(False) self.ui.label_app.setText("MOD Application v%s" % config["version"]) # disable file menu self.ui.act_file_refresh.setEnabled(False) self.ui.act_file_inspect.setEnabled(False) # disable pedalboard menu self.ui.act_pedalboard_new.setEnabled(False) self.ui.act_pedalboard_open.setEnabled(False) self.ui.act_pedalboard_save.setEnabled(False) self.ui.act_pedalboard_save_as.setEnabled(False) self.ui.act_pedalboard_share.setEnabled(False) self.ui.menu_Pedalboard.setEnabled(False) # disable presets menu self.ui.act_presets_new.setEnabled(False) self.ui.act_presets_save.setEnabled(False) self.ui.act_presets_save_as.setEnabled(False) self.ui.menu_Presets.setEnabled(False) # initial stopped state self.slot_backendFinished(-1, -1) # Qt needs this so it properly creates & resizes the webview self.ui.stackedwidget.setCurrentIndex(1) self.ui.stackedwidget.setCurrentIndex(0) # FIXME #self.ui.act_backend_stop.setVisible(False) #self.ui.act_backend_restart.setVisible(False) # ---------------------------------------------------------------------------------------------------- # Set up GUI (special stuff for Mac OS) if MACOS: self.ui.act_file_quit.setMenuRole(QAction.QuitRole) self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole) self.ui.act_help_about.setMenuRole(QAction.AboutRole) #self.ui.menu_Settings.setTitle("Panels") #self.ui.menu_Help.hide() # ---------------------------------------------------------------------------------------------------- # Set up GUI (special stuff for Live-MOD ISO) if USING_LIVE_ISO: self.ui.menubar.hide() self.ui.b_start.hide() self.ui.b_configure.hide() self.ui.b_about.hide() # ---------------------------------------------------------------------------------------------------- # Load Settings self.loadSettings(True) # ---------------------------------------------------------------------------------------------------- # Connect actions to functions self.SIGUSR1.connect(self.slot_handleSIGUSR1) self.SIGTERM.connect(self.slot_handleSIGTERM) self.fProccessBackend.error.connect(self.slot_backendError) self.fProccessBackend.started.connect(self.slot_backendStarted) self.fProccessBackend.finished.connect(self.slot_backendFinished) self.fProccessBackend.readyRead.connect(self.slot_backendRead) self.fWebServerThread.running.connect(self.slot_webServerRunning) self.fWebServerThread.finished.connect(self.slot_webServerFinished) self.ui.menu_Pedalboard.aboutToShow.connect(self.slot_pedalboardCheckOnline) self.ui.act_file_refresh.triggered.connect(self.slot_fileRefresh) self.ui.act_file_inspect.triggered.connect(self.slot_fileInspect) self.ui.act_backend_information.triggered.connect(self.slot_backendInformation) self.ui.act_backend_start.triggered.connect(self.slot_backendStart) self.ui.act_backend_stop.triggered.connect(self.slot_backendStop) self.ui.act_backend_restart.triggered.connect(self.slot_backendRestart) self.ui.act_pedalboard_new.triggered.connect(self.slot_pedalboardNew) self.ui.act_pedalboard_open.triggered.connect(self.slot_pedalboardOpen) self.ui.act_pedalboard_save.triggered.connect(self.slot_pedalboardSave) self.ui.act_pedalboard_save_as.triggered.connect(self.slot_pedalboardSaveAs) self.ui.act_pedalboard_share.triggered.connect(self.slot_pedalboardShare) self.ui.act_settings_configure.triggered.connect(self.slot_configure) self.ui.act_help_about.triggered.connect(self.slot_about) self.ui.act_help_project.triggered.connect(self.slot_showProject) self.ui.act_help_website.triggered.connect(self.slot_showWebsite) self.ui.b_start.clicked.connect(self.slot_backendStart) self.ui.b_configure.clicked.connect(self.slot_configure) self.ui.b_about.clicked.connect(self.slot_about) # force our custom refresh webReloadAction = self.ui.webpage.action(QWebPage.Reload) webReloadAction.triggered.disconnect() webReloadAction.triggered.connect(self.slot_fileRefresh) # ---------------------------------------------------------------------------------------------------- # Final setup self.setProperWindowTitle() SESSION.setupApp(self._pedal_changed_callback) if not "--no-autostart" in sys.argv: QTimer.singleShot(0, self.slot_backendStart) QTimer.singleShot(1, self.fixWebViewSize) def __del__(self): self.stopAndWaitForWebServer() self.stopAndWaitForBackend() def _pedal_changed_callback(self, ok, bundlepath, title): #self.fCurrentBundle = bundlepath self.fCurrentTitle = title or "" #self.updatePresetsMenu() self.setProperWindowTitle() # -------------------------------------------------------------------------------------------------------- # Files (menu actions) @pyqtSlot() def slot_fileRefresh(self): if self.fWebFrame is None: return self.ui.label_progress.setText(self.tr("Refreshing UI...")) self.ui.stackedwidget.setCurrentIndex(0) QTimer.singleShot(0, self.slot_fileRefreshPost) @pyqtSlot() def slot_fileRefreshPost(self): self.ui.webview.loadStarted.connect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.connect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.connect(self.slot_webviewLoadFinished) self.ui.webview.reload() @pyqtSlot() def slot_fileInspect(self): self.ui.webinspector.show() # -------------------------------------------------------------------------------------------------------- # Pedalboard (menu actions) @pyqtSlot() def slot_pedalboardCheckOnline(self): if self.fWebFrame is None: return isOnline = self.fWebFrame.evaluateJavaScript("$('#mod-cloud').hasClass('logged')") if isOnline is None: return print("isOnline is None") self.ui.act_pedalboard_share.setEnabled(isOnline) @pyqtSlot() def slot_pedalboardNew(self): if self.fWebFrame is None: return self.fWebFrame.evaluateJavaScript("desktop.reset()") # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_pedalboardOpen(self): if len(self.fPedalboards) == 0: return QMessageBox.information(self, self.tr("information"), "No pedalboards found") dialog = OpenPedalboardWindow(self, self.fPedalboards) if not dialog.exec_(): return pedalboard = dialog.getSelectedURI() if not pedalboard: return QMessageBox.information(self, self.tr("information"), "Invalid pedalboard selected") try: bundle = get_bundle_dirname(pedalboard) except: return if self.fWebFrame is None: return self.fWebFrame.evaluateJavaScript("desktop.loadPedalboard(\"%s\")" % bundle) def openPedalboardLater(self, filename): try: self.fNextBundle = QFileInfo(filename).absoluteFilePath() self.fCurrentTitle = get_pedalboard_info(self.fNextBundle)['name'] except: self.fNextBundle = "" self.fCurrentTitle = "" # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_pedalboardSave(self, saveAs=False): if self.fWebFrame is None: return self.fWebFrame.evaluateJavaScript("desktop.saveCurrentPedalboard(%s)" % ("true" if saveAs else "false")) @pyqtSlot() def slot_pedalboardSaveAs(self): self.slot_pedalboardSave(True) # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_pedalboardShare(self): if self.fWebFrame is None: return self.fWebFrame.evaluateJavaScript("desktop.shareCurrentPedalboard()") # -------------------------------------------------------------------------------------------------------- # Presets (menu actions) @pyqtSlot() def slot_presetClicked(self): print(self.sender().data()) # -------------------------------------------------------------------------------------------------------- # Settings (menu actions) @pyqtSlot() def slot_configure(self): dialog = SettingsWindow(self, True) if not dialog.exec_(): return self.loadSettings(False) # -------------------------------------------------------------------------------------------------------- # About (menu actions) @pyqtSlot() def slot_about(self): QMessageBox.about(self, self.tr("About"), self.tr(""" <b>MOD Desktop Application</b><br/> <br/> A software to have the complete MOD environment running in your desktop.<br/> (C) 2015-2016 - The MOD Team<br/> <br/> Publications, products, content or services referenced herein or on the website are the exclusive trademarks or servicemarks of MOD.<br/> Other product and company names mentioned in the site may be the trademarks of their respective owners.<br/> <br/> All software is available under the <a href="https://www.gnu.org/licenses/gpl-2.0.html">GPL license</a>.<br/> """)) @pyqtSlot() def slot_showProject(self): QDesktopServices.openUrl(QUrl("https://github.com/moddevices/mod-app")) @pyqtSlot() def slot_showWebsite(self): QDesktopServices.openUrl(QUrl("http://moddevices.com/")) # -------------------------------------------------------------------------------------------------------- # Backend (menu actions) @pyqtSlot() def slot_backendInformation(self): table = """ <table><tr> <td> MOD-UI port: <td></td> %s </td> </tr><tr> <td> Backend address: <td></td> %s </td> </tr></table> """ % (config["port"], "unix:///tmp/mod-app-%s.sock" % config["port"]) QMessageBox.information(self, self.tr("information"), table) @pyqtSlot() def slot_backendStart(self): if self.fProccessBackend.state() != QProcess.NotRunning: print("slot_backendStart ignored") return print("slot_backendStart in progress...") if USING_LIVE_ISO: os.system("jack_wait -w") os.system("jack_load mod-monitor") hostPath = "jack_load" hostArgs = ["-w", "-a", "mod-host"] else: hostPath = self.fSavedSettings[MOD_KEY_HOST_PATH] if hostPath.endswith("ingen"): hostPath = MOD_DEFAULT_HOST_PATH hostArgs = ["-n"] self.fProccessBackend.start(hostPath, hostArgs) @pyqtSlot() def slot_backendStop(self, forced = False): #if self.fPluginCount > 0: #if not forced: #ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" #"Do you want to do this now?"), #QMessageBox.Yes | QMessageBox.No, QMessageBox.No) #if ask != QMessageBox.Yes: #return #self.removeAllPlugins() #self.host.set_engine_about_to_close() #self.host.remove_all_plugins() # testing red color for server stopped self.ui.webview.blockSignals(True) self.ui.webview.setHtml("<html><body bgcolor='green'></body></html>") self.ui.webview.blockSignals(False) self.stopAndWaitForWebServer() self.stopAndWaitForBackend() @pyqtSlot() def slot_backendRestart(self): #self.ui.stackedwidget.setCurrentIndex(0) self.slot_backendStop() #QApplication.instance().processEvents() self.slot_backendStart() # -------------------------------------------------------------------------------------------------------- @pyqtSlot() def slot_backendStarted(self): self.ui.act_backend_start.setEnabled(False) self.ui.act_backend_stop.setEnabled(True) self.ui.act_backend_restart.setEnabled(True) self.ui.w_buttons.setEnabled(False) self.ui.label_progress.setText(self.tr("Loading backend...")) @pyqtSlot(int, QProcess.ExitStatus) def slot_backendFinished(self, exitCode, exitStatus): self.fFirstBackendInit = False self.fStoppingBackend = False self.ui.act_backend_start.setEnabled(True) self.ui.act_backend_stop.setEnabled(False) self.ui.act_backend_restart.setEnabled(False) self.ui.w_buttons.setEnabled(True) self.ui.label_progress.setText("") self.ui.stackedwidget.setCurrentIndex(0) # stop webserver self.stopAndWaitForWebServer() @pyqtSlot(QProcess.ProcessError) def slot_backendError(self, error): firstBackendInit = self.fFirstBackendInit self.fFirstBackendInit = False # stop webserver self.stopAndWaitForWebServer() # crashed while stopping, ignore if error == QProcess.Crashed and self.fStoppingBackend: return errorStr = self.tr("Could not start host backend.\n") + self.getProcessErrorAsString(error) qWarning(errorStr) # don't show error if this is the first time starting the host or using live-iso if firstBackendInit or USING_LIVE_ISO: return # show the error message QMessageBox.critical(self, self.tr("Error"), errorStr) @pyqtSlot() def slot_backendRead(self): #if self.fProccessBackend.state() != QProcess.Running: #return for line in str(self.fProccessBackend.readAllStandardOutput().trimmed(), encoding="utf-8", errors="ignore").strip().split("\n"): line = line.replace("\x1b[0m","").replace("\x1b[0;31m","").replace("\x1b[0;33m","").strip() if not line: continue if self.fSavedSettings[MOD_KEY_HOST_VERBOSE]: print("BACKEND:", line) if line == "mod-host ready!" or line == "mod-host is running.": QTimer.singleShot(0, self.slot_backendStartPhase2) #elif "Listening on socket " in line: #QTimer.singleShot(1000, self.slot_ingenStarted) ##elif "Activated Jack client " in line: ##QTimer.singleShot(1000, self.fWebServerThread.start) #elif "Failed to create UNIX socket" in line or "Could not activate Jack client" in line: ## need to wait for ingen to create sockets so it can delete them on termination #QTimer.singleShot(1000, self.slot_ingenStartError) @pyqtSlot() def slot_backendStartPhase2(self): if self.fProccessBackend.state() == QProcess.NotRunning: return if not self.fNeedsSessionReconnect: # we'll need it for next time self.fNeedsSessionReconnect = True else: # we need it now SESSION.reconnectApp() self.fWebServerThread.start() @pyqtSlot() def slot_backendStartError(self): self.stopAndWaitForBackend() self.slot_backendError(-2) # -------------------------------------------------------------------------------------------------------- # Web Server @pyqtSlot() def slot_webServerRunning(self): try: self.ui.webview.loadStarted.connect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.connect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.connect(self.slot_webviewLoadFinished) except: pass print("webserver running") self.ui.webview.load(QUrl(config["addr"])) @pyqtSlot() def slot_webServerFinished(self): try: self.ui.webview.loadStarted.connect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.connect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.connect(self.slot_webviewLoadFinished) except: pass print("webserver finished") # testing red color for server finished self.ui.webview.blockSignals(True) self.ui.webview.setHtml("<html><body bgcolor='red'></body></html>") self.ui.webview.blockSignals(False) # -------------------------------------------------------------------------------------------------------- # Web View @pyqtSlot() def slot_webviewLoadStarted(self): self.ui.label_progress.setText(self.tr("Loading UI...")) print("load started") @pyqtSlot(int) def slot_webviewLoadProgress(self, progress): self.ui.label_progress.setText(self.tr("Loading UI... %i%%" % progress)) @pyqtSlot(bool) def slot_webviewLoadFinished(self, ok): self.ui.webview.loadStarted.disconnect(self.slot_webviewLoadStarted) self.ui.webview.loadProgress.disconnect(self.slot_webviewLoadProgress) self.ui.webview.loadFinished.disconnect(self.slot_webviewLoadFinished) if ok: # message self.ui.label_progress.setText(self.tr("Loading UI... finished!")) # enable file menu self.ui.act_file_refresh.setEnabled(True) self.ui.act_file_inspect.setEnabled(True) # for js evaulation self.fWebFrame = self.ui.webpage.currentFrame() # postpone app stuff QTimer.singleShot(100, self.slot_webviewPostFinished) else: # message self.ui.label_progress.setText(self.tr("Loading UI... failed!")) # disable file menu self.ui.act_file_refresh.setEnabled(False) self.ui.act_file_inspect.setEnabled(False) # disable pedalboard menu self.ui.act_pedalboard_new.setEnabled(False) self.ui.act_pedalboard_open.setEnabled(False) self.ui.act_pedalboard_save.setEnabled(False) self.ui.act_pedalboard_save_as.setEnabled(False) self.ui.act_pedalboard_share.setEnabled(False) self.ui.menu_Pedalboard.setEnabled(False) # stop js evaulation self.fWebFrame = None # stop backend&server self.stopAndWaitForWebServer() self.stopAndWaitForBackend() print("load finished") @pyqtSlot() def slot_webviewPostFinished(self): if self.fNextBundle: bundle = self.fNextBundle self.fNextBundle = "" self.fWebFrame.evaluateJavaScript("desktop.loadPedalboard(\"%s\")" % bundle) QTimer.singleShot(0, self.slot_webviewPostFinished2) @pyqtSlot() def slot_webviewPostFinished2(self): self.ui.stackedwidget.setCurrentIndex(1) # -------------------------------------------------------------------------------------------------------- # Settings def saveSettings(self): settings = QSettings() settings.setValue("Geometry", self.saveGeometry()) def loadSettings(self, firstTime): qsettings = QSettings() websettings = self.ui.webview.settings() self.fSavedSettings = { # Main MOD_KEY_MAIN_PROJECT_FOLDER: qsettings.value(MOD_KEY_MAIN_PROJECT_FOLDER, MOD_DEFAULT_MAIN_PROJECT_FOLDER, type=str), MOD_KEY_MAIN_REFRESH_INTERVAL: qsettings.value(MOD_KEY_MAIN_REFRESH_INTERVAL, MOD_DEFAULT_MAIN_REFRESH_INTERVAL, type=int), # Host MOD_KEY_HOST_VERBOSE: qsettings.value(MOD_KEY_HOST_VERBOSE, MOD_DEFAULT_HOST_VERBOSE, type=bool), MOD_KEY_HOST_PATH: qsettings.value(MOD_KEY_HOST_PATH, MOD_DEFAULT_HOST_PATH, type=str), # WebView MOD_KEY_WEBVIEW_INSPECTOR: qsettings.value(MOD_KEY_WEBVIEW_INSPECTOR, MOD_DEFAULT_WEBVIEW_INSPECTOR, type=bool), MOD_KEY_WEBVIEW_VERBOSE: qsettings.value(MOD_KEY_WEBVIEW_VERBOSE, MOD_DEFAULT_WEBVIEW_VERBOSE, type=bool), MOD_KEY_WEBVIEW_SHOW_INSPECTOR: qsettings.value(MOD_KEY_WEBVIEW_SHOW_INSPECTOR, MOD_DEFAULT_WEBVIEW_SHOW_INSPECTOR, type=bool) } inspectorEnabled = self.fSavedSettings[MOD_KEY_WEBVIEW_INSPECTOR] and not USING_LIVE_ISO websettings.setAttribute(QWebSettings.DeveloperExtrasEnabled, inspectorEnabled) if firstTime: if qsettings.contains("Geometry"): self.restoreGeometry(qsettings.value("Geometry", "")) else: self.setWindowState(self.windowState() | Qt.WindowMaximized) if inspectorEnabled and self.fSavedSettings[MOD_KEY_WEBVIEW_SHOW_INSPECTOR]: QTimer.singleShot(1000, self.ui.webinspector.show) self.ui.act_file_inspect.setVisible(inspectorEnabled) if self.fIdleTimerId != 0: self.killTimer(self.fIdleTimerId) self.fIdleTimerId = self.startTimer(self.fSavedSettings[MOD_KEY_MAIN_REFRESH_INTERVAL]) # -------------------------------------------------------------------------------------------------------- # Misc @pyqtSlot() def slot_handleSIGUSR1(self): print("Got SIGUSR1 -> Saving project now") self.slot_pedalboardSave() @pyqtSlot() def slot_handleSIGTERM(self): print("Got SIGTERM -> Closing now") self.close() # -------------------------------------------------------------------------------------------------------- # Qt events def closeEvent(self, event): if self.fIdleTimerId != 0: self.killTimer(self.fIdleTimerId) self.fIdleTimerId = 0 self.saveSettings() self.slot_backendStop(True) QMainWindow.closeEvent(self, event) # Needed in case the web inspector is still alive #self.ui.webinspector.close() QApplication.instance().quit() def timerEvent(self, event): if event.timerId() == self.fIdleTimerId: pass QMainWindow.timerEvent(self, event) def resizeEvent(self, event): QMainWindow.resizeEvent(self, event) self.fixWebViewSize() # -------------------------------------------------------------------------------------------------------- # Internal stuff def getProcessErrorAsString(self, error): if error == -2: return self.tr("Ingen failed to create UNIX socket.") if error == QProcess.FailedToStart: return self.tr("Process failed to start.") if error == QProcess.Crashed: return self.tr("Process crashed.") if error == QProcess.Timedout: return self.tr("Process timed out.") if error == QProcess.WriteError: return self.tr("Process write error.") return self.tr("Unkown error.") def fixWebViewSize(self): if self.ui.stackedwidget.currentIndex() == 1: return size = self.ui.swp_intro.size() self.ui.swp_webview.resize(size) self.ui.webview.resize(size) self.ui.webpage.setViewportSize(size) def stopAndWaitForBackend(self): if self.fProccessBackend.state() == QProcess.NotRunning: return self.fStoppingBackend = True self.fProccessBackend.terminate() if not self.fProccessBackend.waitForFinished(2000): qWarning("Backend failed top stop cleanly, forced kill") self.fProccessBackend.kill() def stopAndWaitForWebServer(self): if not self.fWebServerThread.isRunning(): return if not self.fWebServerThread.stopWait(): qWarning("WebServer Thread failed top stop cleanly, forced terminate") self.fWebServerThread.terminate() def setProperWindowTitle(self): title = "MOD Application" if self.fCurrentTitle: title += " - %s" % self.fCurrentTitle self.setWindowTitle(title)
class DialogueNew(QtGui.QDialog, FORM_CLASS): SOURCES = ["ASCII squared raster", "CSV file", "Python function"] METHODS = ["Multi-quadratic", "Nearest neighbour"] METHODS_ABRV = ["mq", "nn"] iface = None def __init__(self, parent=None): """Constructor.""" super(DialogueNew, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.comboBoxSource.addItems(self.SOURCES) self.comboBoxSource.currentIndexChanged.connect(self.sourceChanged) self.comboBoxMethod.addItems(self.METHODS) self.buttonBox.button(QDialogButtonBox.Ok).setText("Run") self.buttonBox.button(QDialogButtonBox.Cancel).setText("Exit") self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) # Activate file search buttons self.pushButtonSource.clicked.connect(self.selectFileSource) self.pushButtonOutput.clicked.connect(self.selectFileOutput) self.resize(520, 430) def accept(self): if(not self.checkOptions()): return self.resize(520, 610) if(self.comboBoxSource.currentText() == self.SOURCES[0]): if(self.comboBoxMethod == self.METHODS[0]): meth_abv = self.METHODS_ABRV[0] else: meth_abv = self.METHODS_ABRV[1] args = QStringList() args.append("-i" + self.filePathSource.text()) args.append("-o" + self.filePathOutput.text()) args.append("-m" + meth_abv) self.proc = QProcess() self.proc.start("asc2hasc", args) self.proc.setProcessChannelMode(QProcess.MergedChannels); QObject.connect(self.proc, SIGNAL("readyReadStandardOutput()"), self, SLOT("readStdOutput()")) #self.setGeometry(self._parent.x(), self._parent.y(), self.width(), 610) if(self.comboBoxSource.currentText() == self.SOURCES[1]): args = QStringList() args.append("-i" + self.filePathSource.text()) args.append("-o" + self.filePathOutput.text()) args.append("-s" + self.cellSide.text()) self.proc = QProcess() self.proc.start("csv2hasc", args) self.proc.setProcessChannelMode(QProcess.MergedChannels); QObject.connect(self.proc, SIGNAL("readyReadStandardOutput()"), self, SLOT("readStdOutput()")) if(self.comboBoxSource.currentText() == self.SOURCES[2]): args = QStringList() args.append("-m" + self.filePathSource.text()) args.append("-o" + self.filePathOutput.text()) args.append("-f" + self.pythonFunction.text()) args.append("-s" + self.cellSide.text()) args.append("-x" + self.eastingRight.text()) args.append("-X" + self.eastingLeft.text()) args.append("-y" + self.northingBottom.text()) args.append("-Y" + self.northingTop.text()) self.proc = QProcess() self.proc.start("surface2hasc", args) self.proc.setProcessChannelMode(QProcess.MergedChannels); QObject.connect(self.proc, SIGNAL("readyReadStandardOutput()"), self, SLOT("readStdOutput()")) @pyqtSlot() def readStdOutput(self): self.commandOutput.append(QString(self.proc.readAllStandardOutput())) def reject(self): self.done(0) def selectFileSource(self): extension = "" if(self.comboBoxSource.currentText() == self.SOURCES[0]): extension = '*.asc' elif(self.comboBoxSource.currentText() == self.SOURCES[1]): extension = '*.csv' elif(self.comboBoxSource.currentText() == self.SOURCES[2]): extension = '*.py' self.fileNameSource = QFileDialog.getOpenFileName(self, "Select file ","", extension); self.filePathSource.setText(self.fileNameSource) def selectFileOutput(self): self.fileNameOutput = QFileDialog.getOpenFileName(self, "Select file ","", "*.hasc"); self.filePathOutput.setText(self.fileNameOutput) def sourceChanged(self, i): if(self.comboBoxSource.currentText() == self.SOURCES[0]): self.pushButtonSource.setEnabled(True) self.labelExtent.setEnabled(False) self.labelNorthingTop.setEnabled(False) self.labelNorthingBottom.setEnabled(False) self.labelEastingRight.setEnabled(False) self.labelEastingLeft.setEnabled(False) self.northingTop.setEnabled(False) self.northingBottom.setEnabled(False) self.eastingRight.setEnabled(False) self.eastingLeft.setEnabled(False) self.labelMethod.setEnabled(True) self.comboBoxMethod.setEnabled(True) self.pythonFunction.setEnabled(False) self.labelPythonFunction.setEnabled(False) self.cellSide.setEnabled(False) self.labelCellSide.setEnabled(False) elif(self.comboBoxSource.currentText() == self.SOURCES[1]): self.pushButtonSource.setEnabled(True) self.labelExtent.setEnabled(False) self.labelNorthingTop.setEnabled(False) self.labelNorthingBottom.setEnabled(False) self.labelEastingRight.setEnabled(False) self.labelEastingLeft.setEnabled(False) self.northingTop.setEnabled(False) self.northingBottom.setEnabled(False) self.eastingRight.setEnabled(False) self.eastingLeft.setEnabled(False) self.labelMethod.setEnabled(False) self.comboBoxMethod.setEnabled(False) self.pythonFunction.setEnabled(False) self.labelPythonFunction.setEnabled(False) self.cellSide.setEnabled(True) self.labelCellSide.setEnabled(True) elif(self.comboBoxSource.currentText() == self.SOURCES[2]): self.pushButtonSource.setEnabled(True) self.labelExtent.setEnabled(True) self.labelNorthingTop.setEnabled(True) self.labelNorthingBottom.setEnabled(True) self.labelEastingRight.setEnabled(True) self.labelEastingLeft.setEnabled(True) self.northingTop.setEnabled(True) self.northingBottom.setEnabled(True) self.eastingRight.setEnabled(True) self.eastingLeft.setEnabled(True) self.labelMethod.setEnabled(False) self.comboBoxMethod.setEnabled(False) self.pythonFunction.setEnabled(True) self.labelPythonFunction.setEnabled(True) self.cellSide.setEnabled(True) self.labelCellSide.setEnabled(True) else: return def checkOptions(self): if(self.comboBoxSource.currentText() == ""): self.showErrorMessage("Please select a source type.") return False if(self.filePathOutput.text() == None or self.filePathOutput.text() == ""): self.showErrorMessage("Please provide an output file.") return False if(self.filePathSource.text() == None or self.filePathSource.text() == ""): self.showErrorMessage("Please select a source file.") return False if(self.comboBoxSource.currentText() != self.SOURCES[0]): if(self.northingTop.text() == None or self.northingTop.text() == "" or self.northingBottom.text() == None or self.northingBottom.text() == "" or self.eastingRight.text() == None or self.eastingRight.text() == "" or self.eastingLeft.text() == None or self.eastingLeft.text() == ""): self.showErrorMessage("Extent is mandatory for this source type.") return False if(self.cellSide.text() == None or self.cellSide.text() == ""): self.showErrorMessage("A cell side length is required for this source type.") return False if(self.comboBoxSource.currentText() == self.SOURCES[2]): if(self.pythonFunction.text() == None or self.pythonFunction.text() == ""): self.showErrorMessage("Please select a Python function in the module.") return False return True def showErrorMessage(self, mesg): msgBox = QMessageBox() msgBox.setIcon(QMessageBox.Warning) msgBox.setText(mesg) msgBox.setWindowTitle("Unable to proceed") msgBox.exec_()
def interpret(self, command): """Interprets command Returns turple (output, exitCode) """ print '~~~~~~~~~ interpret' ec = pConsoleCommand.NotFound output, ec = pConsoleCommand.interpret( command) parts = self.parseCommands( command ) if parts: cmd = parts.takeFirst() else: cmd = '' if ec != pConsoleCommand.NotFound : # nothing to do pass elif cmd == "ls" : if sys.platform.startswith('win'): cmd = "dir %s" % \ " ".join(pConsoleCommand.quotedStringList(parts)).trim() else: cmd = "dir %s" % " ".join( \ pConsoleCommand.quotedStringList(parts)).trimmed() process = QProcess() process.setProcessChannelMode( QProcess.MergedChannels ) process.start( cmd ) process.waitForStarted() process.waitForFinished() output = process.readAll().trimmed() ec = process.exitCode() elif cmd == "echo" : if parts: output = "\n".join(parts) ec = pConsoleCommand.Error else: output = pConsole.tr(console, "No argument given" ) ec = pConsoleCommand.Success elif cmd == "quit": output = pConsole.tr(console, "Quitting the application..." ) ec = pConsoleCommand.Success QTimer.singleShot(1000, qApp.quit() ) elif cmd == "style" : if parts.count() != 1 : output = pConsole.tr(console, "%s take only 1 parameter, %d given" % (cmd, len(parts)) ) ec = pConsoleCommand.Error elif parts[-1] == "list" : output = pConsole.tr(console, "Available styles:\n%s" % '\n'.join(QStyleFactory.keys()) ) ec = pConsoleCommand.Success else: styleExists = parts[-1].lower() in \ [key.lower() for key in QStyleFactory.keys()] if styleExists: output = pConsole.tr(console, "Setting style to %s..." % parts[-1]) self.mMainWindow.setCurrentStyle( parts[-1] ) ec = pConsoleCommand.Success else: output = pConsole.tr(console, "This style does not exists" ) ec = pConsoleCommand.Error return (output, ec)