Skip to content

Instantly share code, notes, and snippets.

@meoow
Created August 14, 2014 03:32
Show Gist options
  • Save meoow/4fd695a7cfaf1b429b3a to your computer and use it in GitHub Desktop.
Save meoow/4fd695a7cfaf1b429b3a to your computer and use it in GitHub Desktop.
Simple GUI for xunlei-lixian
#!/usr/bin/env python2.7
from PyQt4.QtCore import SIGNAL, pyqtSignal, QObject, QString
from PyQt4.QtGui import (QApplication, QDialog,
QTextBrowser, QPushButton, QVBoxLayout,
QLineEdit, QGridLayout, QTextCursor,
QLabel, QHBoxLayout, QFileDialog,
QCheckBox , QSizePolicy , QSpacerItem,
QGroupBox, QComboBox, QSpinBox)
import sys,os,re
import subprocess as subp
import platform as pf
import getopt
import argparse
from lixian_config import Config, LIXIAN_DEFAULT_CONFIG
#from lixian_cli import execute_command as lx_exec
#sys.stderr = codecs.lookup('utf-8')[-1](sys.stderr)
class EmitStream(QObject):
textWritten = pyqtSignal(str)
def write(self, text):
self.textWritten.emit(text.decode('gbk'))
class Form(QDialog):
def __init__(self,parent=None):
super(Form, self).__init__(parent)
# self.stdout = EmitStream(textWritten=self.textOutput)
# sys.stderr = EmitStream(textWritten=self.textOutputErr)
self.pyInterpreDict = {'Linux':'python2.7','Windows':'python.exe'}
self.lx_exec = '{0} lixian_cli.py'.format(self.pyInterpreDict.get(pf.system(),'python2'))
self.matchCompletedTask = re.compile(r' completed$')
self.toolList = [u'aria2',u'wget',u'asyn']
self.lxConfig = Config(LIXIAN_DEFAULT_CONFIG)
savedDir = self.lxConfig.get('output-dir')
if savedDir is None:
savedDir = os.environ['HOME']
continue_ = self.lxConfig.get('continue')
delete_ = self.lxConfig.get('delete')
tool_ = self.lxConfig.get('tool')
aria2opts = self.lxConfig.get("aria2-opts")
#addTaskBtnGrp = QButtonGroup()
addTaskButton = QPushButton(u"Add &Task")
addTaskButton.setFixedWidth(86)
addBtTaskButton = QPushButton(u'BT File')
#addTaskBtnGrp.addButton(addTaskButton,1)
#addTaskBtnGrp.addButton(addBtTaskButton,2)
downloadButton = QPushButton(u'&Go')
downloadButton.setFixedWidth(86)
addConfigButton = QPushButton(u'Add &Config')
addConfigButton.setFixedWidth(86)
listButton = QPushButton(u'&List')
listButton.setFixedWidth(86)
deleteButton = QPushButton(u'&Delete')
deleteButton.setFixedWidth(86)
clearButton = QPushButton(u'&Clear')
clearButton.setFixedWidth(86)
outputDirBrButton = QPushButton(u'&Browse')
outputDirBrButton.setFixedWidth(86)
self.taskLine = QLineEdit()
self.downloadLine = QLineEdit()
self.configLine = QLineEdit()
self.deleteLine = QLineEdit()
self.outputDirLine = QLineEdit()
configToggleSpacerItem=QSpacerItem(1,1,QSizePolicy.Expanding)
self.continueCheckBox = QCheckBox(u'Continue')
self.deleteCheckBox = QCheckBox(u'Delete')
toolLabel = QLabel(u'Tool:')
self.toolComboBox = QComboBox()
self.toolComboBox.addItems(self.toolList)
# self.toolLineEdit.setMaximumWidth(50)
serversLabel = QLabel(u'Servers:')
connectionsLabel = QLabel(u'Connections:')
self.toolxSpinBox = QSpinBox()
self.toolxSpinBox.setRange(1,16)
self.toolsSpinBox = QSpinBox()
self.toolsSpinBox.setRange(1,16)
taskLabel = QLabel(u'URL: ')
downLabel = QLabel(u'Download: ')
outputDirLabel = QLabel(u'Output Dir: ')
blankLabel = QLabel()
configLineLayout = QHBoxLayout()
deleteLineLayout = QHBoxLayout()
configLineLayout.addWidget(self.configLine)
configLineLayout.addWidget(addConfigButton)
deleteLineLayout.addWidget(self.deleteLine)
deleteLineLayout.addWidget(deleteButton)
self.taskLine.setMinimumWidth(300)
operationAeraLayout = QGridLayout()
operationAeraLayout.addWidget(taskLabel,0,0)
operationAeraLayout.addWidget(self.taskLine,0,1)
operationAeraLayout.addWidget(addTaskButton,0,2)
operationAeraLayout.addWidget(downLabel,1,0)
operationAeraLayout.addWidget(self.downloadLine,1,1)
operationAeraLayout.addWidget(downloadButton,1,2)
operationAeraLayout.addWidget(outputDirLabel,2,0)
operationAeraLayout.addWidget(self.outputDirLine,2,1)
operationAeraLayout.addWidget(outputDirBrButton,2,2)
configToggleGrpBox = QGroupBox('Configuration')
configToggleLayout = QHBoxLayout(configToggleGrpBox)
configToggleLayout.addWidget(self.continueCheckBox)
configToggleLayout.addWidget(self.deleteCheckBox)
configToggleLayout.addWidget(toolLabel)
configToggleLayout.addWidget(self.toolComboBox)
configToggleLayout.addWidget(connectionsLabel)
configToggleLayout.addWidget(self.toolsSpinBox)
configToggleLayout.addWidget(serversLabel)
configToggleLayout.addWidget(self.toolxSpinBox)
configToggleLayout.addItem(configToggleSpacerItem)
configToggleLayout.addStretch()
cfgDltLstClrLayout = QGridLayout()
cfgDltLstClrLayout.addLayout(configLineLayout,0,0)
cfgDltLstClrLayout.addWidget(listButton,0,1)
cfgDltLstClrLayout.addWidget(blankLabel,0,2)
cfgDltLstClrLayout.addLayout(deleteLineLayout,1,0)
cfgDltLstClrLayout.addWidget(clearButton,1,1)
cfgDltLstClrLayout.addWidget(blankLabel,1,2)
self.fuckTextBrsr = QTextBrowser()
layout = QVBoxLayout()
layout.addLayout(operationAeraLayout)
layout.addWidget(configToggleGrpBox)
layout.addLayout(cfgDltLstClrLayout)
layout.addWidget(self.fuckTextBrsr)
self.setLayout(layout)
self.setWindowTitle('Offline Thunder Downloader for TriMeow Church')
self.osQuoteDict = {'Linux':"'","Windows":'"'}
self.codecDict = {'Linux':'utf-8', 'Windows':'gbk'}
self.outputDirLine.setText(savedDir)
if continue_:
self.continueCheckBox.setChecked(1)
if delete_:
self.deleteCheckBox.setChecked(1)
connectionsNum, serversNum, dummy = self.parseAria2Opts(aria2opts)
self.toolxSpinBox.setValue(connectionsNum)
self.toolsSpinBox.setValue(serversNum)
self.toolComboBox.setCurrentIndex(self.toolList.index(tool_ or 'aria2'))
self.saveConfig()
self.connect(addTaskButton,SIGNAL('clicked()'),self.addURLTask)
self.connect(downloadButton,SIGNAL('clicked()'),self.download)
self.connect(addConfigButton,SIGNAL('clicked()'),self.addConfig)
self.connect(listButton, SIGNAL('clicked()'),self.listFile)
self.connect(deleteButton, SIGNAL('clicked()'),self.deleteTask)
self.connect(clearButton, SIGNAL('clicked()'),self.clearOutput)
self.connect(outputDirBrButton, SIGNAL('clicked()'),self.browseDir)
self.connect(self.continueCheckBox, SIGNAL('stateChanged(int)'), self.toggleContinue)
self.connect(self.deleteCheckBox, SIGNAL('stateChanged(int)'), self.toggleDelete)
self.connect(self.toolxSpinBox, SIGNAL('valueChanged(int)'), self.changeSoX)
self.connect(self.toolsSpinBox, SIGNAL('valueChanged(int)'),self.changeSoX)
self.connect(self.toolComboBox, SIGNAL('currentIndexChanged(int)'),self.changeTool)
def changeSoX(self,verbose=True):
dumm1, dumm2, args = self.parseAria2Opts(self.lxConfig.get('aria2-opts'))
self.lxConfig.put('aria2-opts',' '.join( args+['-x',str(self.toolxSpinBox.value()),'-s',str(self.toolsSpinBox.value())]))
return
def changeTool(self):
self.lxConfig.put('tool',self.toolList[self.toolComboBox.currentIndex()])
def toggleContinue(self,verbose=True):
if self.continueCheckBox.isChecked():
self.lxConfig.put('continue')
else:
self.lxConfig.delete('continue')
return
def toggleDelete(self,verbose=True):
if self.deleteCheckBox.isChecked():
self.lxConfig.put('delete')
else:
self.lxConfig.delete('delete')
return
def addConfig(self):
infos, exitStatus = self.commandString2Pipe('{0} config {1}'.format(self.lx_exec, unicode(self.configLine.text())))
self.htmlOutput('<font color=darkblue>{0}</font>'.format(infos))
if exitStatus is None:
self.configLine.clear()
def download(self):
result = subp.call('{0} download {1}'.format(self.lx_exec, unicode(self.downloadLine.text())),shell=True)
if not result.real:
self.downloadLine.clear()
return
def addURLTask(self):
infos, exitStatus = self.commandString2Pipe('''{0} add {2}{1}{2}'''.format(self.lx_exec, unicode(self.taskLine.text()),self.osQuoteDict.get(pf.system(),"'")))
self.textOutput(infos)
if exitStatus is None:
self.taskLine.clear()
return
def addBTTask(self):
fileName = unicode(QFileDialog.getOpenFileName(self,"choose bt file"))
if fileName:
self.taskLine.setText(fileName)
infos, exitStatus = self.commandString2Pipe('''{0} add --bt {2}{1}{2}'''.format(self.lx_exec, fileName, self.osQuoteDict.get(pf.system(),"'")))
self.textOutput(infos)
if exitStatus is None:
self.taskLine.clear()
return
def listFile(self):
self.fuckTextBrsr.clear()
infos, exitStatus = self.commandString2Pipe('{0} list'.format(self.lx_exec))
infoLines = infos.split('\n')
for index, line in enumerate(infoLines):
if not self.matchCompletedTask.search(line):
infoLines[index] = u'<i>{0}</i>'.format(line)
self.htmlOutput('<br>'.join(infoLines))
return
def browseDir(self):
dirName = unicode(QFileDialog.getExistingDirectory(self))
if dirName:
self.outputDirLine.setText(dirName)
infos, exitStatus = self.commandString2Pipe('{0} config output-dir={2}{1}{2}'.format(self.lx_exec, unicode(self.outputDirLine.text()), self.osQuoteDict.get(pf.system(),"'") ))
self.htmlOutput('<font color=darkblue>{0}</font><br>'.format(infos))
def deleteTask(self):
infos, exitStatus = self.commandString2Pipe('{0} delete {1}'.format(self.lx_exec,unicode(self.deleteLine.text())))
self.textOutput(infos)
if exitStatus is None:
self.deleteLine.clear()
def commandString2Pipe(self,text):
cmdPipe = os.popen(text)
return (''.join(x.decode(self.codecDict.get(pf.system(),'utf-8')) for x in cmdPipe.readlines()), cmdPipe.close())
def textOutput(self,text):
self.fuckTextBrsr.moveCursor(QTextCursor.End)
self.fuckTextBrsr.insertPlainText(u'{0}\n'.format(text))
self.fuckTextBrsr.moveCursor(QTextCursor.End)
def htmlOutput(self,text):
self.fuckTextBrsr.moveCursor(QTextCursor.End)
self.fuckTextBrsr.insertHtml(u'{0}\n'.format(text))
self.fuckTextBrsr.moveCursor(QTextCursor.End)
def textOutputErr(self, text):
self.fuckTextBrsr.moveCursor(QTextCursor.End)
self.fuckTextBrsr.insertHtml('<font color=red>{0}</font><br>'.format(text))
self.fuckTextBrsr.moveCursor(QTextCursor.End)
def clearOutput(self):
self.fuckTextBrsr.clear()
# def parseAria2Opts(self,args):
# try:
# opts, args = getopt.getopt(re.split(r'\s+|=',args),'x:s:',['max-connection-per-server','split'])
# except getopt.GetoptError:
# pass
# for o,a in opts:
# if o in ('-x','--max-connection-per-server'):
# x=int(a)
# elif o in ('-s','--split'):
# s=int(a)
# return x,s
def parseAria2Opts(self,args):
parser = argparse.ArgumentParser()
parser.add_argument('-x','--max-connection-per-server',dest='x',
type=int,default=10)
parser.add_argument('-s','--split',dest='s',type=int,default=10)
opts, args = parser.parse_known_args(re.split(r'\s+|=',args or ""))
return opts.x, opts.s, args
def saveConfig(self):
continue_ = self.continueCheckBox.isChecked()
delete_ = self.deleteCheckBox.isChecked()
tool = self.toolList[self.toolComboBox.currentIndex()]
if continue_:
self.lxConfig.put('continue')
else:
self.lxConfig.delete('continue')
if delete_:
self.lxConfig.put('delete')
else:
self.lxConfig.delete('delete')
self.lxConfig.put('tool',tool)
self.lxConfig.put('output-dir',self.outputDirLine.text())
self.changeSoX()
return
if __name__ == '__main__':
import sys,os
if sys.version_info[0] != 2:
raise SystemExit('Must use Python 2 series')
if os.isatty(sys.stdout.fileno()):
app = QApplication(sys.argv)
form = Form()
form.show()
form.exec_()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment