Created
June 17, 2015 07:08
-
-
Save mcchae/709063c21812ba637620 to your computer and use it in GitHub Desktop.
diagpy : Simple Python dialog based Text UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# for ubuntu 12.04 LTS | |
sudo apt-get install dialog | |
sudo -H pip install python2-pythondialog | |
## for CentOS 5.4 386 | |
#sudo yum install dialog | |
#sudo -H /opt/bin/pip install python2-pythondialog | |
## If above pip fail with next message | |
## ... | |
## CompressionError: bz2 module is not available | |
## then install bzip2-devel and rebuild python 2.7 | |
#sudo yum install bzip2-devel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ # diagpy - python dialog dict : menu | |
# Warning: | |
# - Number of menu cannot be exceed 25 | |
'intro':{ | |
'title':u'diagpy demo', | |
'text':u'diagpy text', | |
'sleep':1.0, | |
}, | |
'menu':[ | |
{ | |
'title':u'Top title 1: yes/no?', | |
'dialog':'yesno', | |
'key': '1', | |
}, | |
{ | |
'title':u'Top title 2: text', | |
'dialog':'inputbox', | |
'key': '2', | |
}, | |
{ | |
'title':u'Top title 3: checklist', | |
'dialog':'checklist', | |
'choices':[ | |
["1", "Item 1", False], | |
["2", "Item 2", True], | |
["3", "Item 3", True] | |
], | |
'key': '3', | |
}, | |
{ | |
'title':u'Top title 4: menu', | |
'menu':[ | |
{ | |
'title':u'4 title 1: yes/no?', | |
'dialog':'yesno', | |
'key': '4-1', | |
}, | |
{ | |
'title':u'4 title 2: text', | |
'dialog':'inputbox', | |
'key': '4-2', | |
}, | |
{ | |
'title':u'4 title 3: checklist', | |
'dialog':'checklist', | |
'choices':[ | |
["1", "Item 1", False], | |
["2", "Item 2", True], | |
["3", "Item 3", True] | |
], | |
'key': '4-3', | |
}, | |
{ | |
'title':u'4 title 4: menu', | |
'menu':[ | |
{ | |
'title':u'4-4 title 1: yes/no?', | |
'dialog':'yesno', | |
'key': '4-4-1', | |
}, | |
{ | |
'title':u'4-4 title 2: text', | |
'dialog':'inputbox', | |
'key': '4-4-2', | |
}, | |
{ | |
'title':u'4-4 title 3: checklist', | |
'dialog':'checklist', | |
'choices':[ | |
["1", "Item 1", False], | |
["2", "Item 2", True], | |
["3", "Item 3", True] | |
], | |
'key': '4-4-3', | |
}, | |
], | |
}, | |
], | |
}, | |
{ | |
'title':u'Top title 5: radio', | |
'dialog':'menu', | |
'choices':[ | |
["1", "Item 1"], | |
["2", "Item 2"], | |
["3", "Item 3"] | |
], | |
'key': '5', | |
}, | |
{ | |
'title':u'Top title 6: password', | |
'dialog':'passwordbox', | |
'key': '6', | |
}, | |
], | |
'postjob':[ | |
{ | |
'name':'Do first job', | |
'cmd':'echo', | |
'key-parameters':[ | |
'1', | |
'2', | |
'3', | |
'5', | |
'6', | |
], | |
#'after-stop':True, | |
}, | |
{ | |
'name':'Do Second job', | |
'cmd':'echo', | |
'key-parameters':[ | |
'4-1', | |
'4-2', | |
'4-3', | |
], | |
}, | |
{ | |
'name':'Do Third job', | |
'cmd':'echo', | |
'key-parameters':[ | |
'4-4-1', | |
'4-4-2', | |
'4-4-3', | |
], | |
# 'after-stop':True, | |
}, | |
], | |
'bye':{ | |
'title':u'bye bye', | |
'text':u'diagpy bye~~', | |
'sleep':1.0, | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf8 -*- | |
""" | |
==================================== | |
:mod:`diagpy.py` Simple Python dialog based Text UI | |
==================================== | |
.. moduleauthor:: 채문창 <mcchae@gmail.com> | |
""" | |
__author__ = "MoonChang Chae <mcchae@gmail.com>" | |
########################################################################################## | |
import os | |
import time | |
import signal | |
import traceback | |
import pickle | |
import subprocess | |
from dialog import Dialog | |
import locale | |
########################################################################################## | |
def signal_handler(signal, frame): | |
"""시그널 핸들러 (프로세스 종료) | |
""" | |
#print 'You pressed Ctrl+C!' | |
# sys.exit(0) | |
pass | |
########################################################################################## | |
class DiagPy(object): | |
#===================================================================================== | |
SELECTED=' [selected]' | |
#===================================================================================== | |
def __init__(self, dicfile=None, outdicfile=None): | |
if dicfile is None: | |
folder = os.path.dirname(os.path.abspath(__file__)) | |
dicfile = '%s/diagpy.dic' % folder | |
self.dicfile = dicfile | |
if not os.path.exists(dicfile): | |
raise IOError('Cannot dic file <%s>' % dicfile) | |
with open(dicfile) as ifp: | |
dicstr = ifp.read() | |
if outdicfile is None: | |
folder = os.path.dirname(os.path.abspath(__file__)) | |
outdicfile = '%s/diagpy.pkl' % folder | |
self.outdicfile = outdicfile | |
if os.path.exists(outdicfile): | |
with open(outdicfile) as ifp: | |
self.outdic = pickle.load(ifp) | |
else: | |
self.outdic = {} | |
try: | |
self.dic = eval(dicstr) | |
except Exception,e: | |
raise ValueError('Invalid dictionary. check <%s>\n%s' % (dicfile, e)) | |
self.d = Dialog(dialog="dialog") | |
self.max_height, self.max_width = self.d.maxsize() | |
# self.d.msgbox('max_width=%s, max_height=%s' % (self.max_width, self.max_height)) | |
#===================================================================================== | |
def _save(self): | |
with open(self.outdicfile, 'w') as ofp: | |
pickle.dump(self.outdic, ofp) | |
return True | |
#===================================================================================== | |
def intro(self): | |
if self.dic.has_key('intro'): | |
_intro = self.dic['intro'] | |
_title = _intro.get('title','Intro Title Missing!') | |
_text = _intro.get('text','Intro Text Missing!') | |
self._text = _text | |
_sleep = _intro.get('sleep',1.0) | |
self.d.infobox(_text, title=_title, | |
height=0, width=0) | |
# height=self.max_height, width=self.max_width) | |
time.sleep(_sleep) | |
#===================================================================================== | |
def bye(self, msg=None): | |
if self.dic.has_key('bye'): | |
_bye = self.dic['bye'] | |
_title = _bye.get('title','Intro Title Missing!') | |
_sleep = _bye.get('sleep',1.0) | |
if msg: | |
_text = msg | |
self.d.infobox(_text, title=_title, height=self.max_height, width=self.max_width) | |
else: | |
_text = _bye.get('text','Intro Text Missing!') | |
self.d.infobox(_text, title=_title, height=0, width=0) | |
time.sleep(_sleep) | |
#===================================================================================== | |
def getTopMenu(self): | |
return self.dic.get('menu',[]) | |
#===================================================================================== | |
def showMenu(self, menu, head='head title', depth=0): | |
self._menudic = {} | |
_choices = [] | |
for i, mi in enumerate(menu): | |
_ord = chr(ord('A')+i) | |
_title = mi.get('title','Missing Title...') | |
_key = mi.get('key') | |
_val = self.outdic.get(_key) | |
_dialog = mi.get('dialog') | |
# self.d.infobox('_val=<%s>' % _val) | |
if _val is not None: | |
if _dialog.lower() == 'yesno': | |
_title += ' [%s]' % ('yes' if _val else 'no') | |
elif _dialog.lower() == 'inputbox': | |
_title += ' ["%s"]' % _val | |
elif _dialog.lower() == 'menu': | |
_chs = mi.get('choices',[]) | |
for _c in _chs: | |
if _val == _c[0]: | |
_title += ' ["%s"]' % _c[1] | |
break | |
self._menudic[_ord] = mi | |
_choices.append((_ord, _title)) | |
_height = self.max_height | |
_width = self.max_width | |
_cancel_label='Parent' | |
if depth <= 0: | |
_cancel_label='Exit' | |
code, tag = self.d.menu(head, | |
height=_height, width=_width, | |
choices=_choices, cancel_label=_cancel_label, | |
extra_button=True, extra_label='Save&Execute') | |
else: | |
code, tag = self.d.menu(head, | |
height=_height, width=_width, | |
choices=_choices, cancel_label=_cancel_label) | |
# d_res(self.d, code, tag) | |
return code, tag | |
#===================================================================================== | |
def showDialog(self, mi): | |
_dialog = mi.get('dialog') | |
_key = mi.get('key') | |
_val = self.outdic.get(_key) | |
_title = mi.get('title','Missing Title') | |
if _dialog is None or _key is None: | |
return False | |
_init = self.outdic.get(_key) | |
_rc = False | |
if _dialog.lower() == 'yesno': | |
code = self.d.yesno(_title) | |
_rc = code == self.d.OK | |
self.outdic[_key] = _rc | |
elif _dialog.lower() == 'inputbox': | |
if not _init: _init = '' | |
code, answer = self.d.inputbox(_title, init=_init) | |
_rc = code == self.d.OK | |
if code == self.d.OK: | |
self.outdic[_key] = answer | |
elif _dialog.lower() == 'checklist': | |
_choices = mi.get('choices',[]) | |
if _val is not None: | |
for _c in _choices: | |
_c[2] = False | |
for _v in _val: | |
if _v == _c[0]: | |
_c[2] = True | |
break | |
code, tag = self.d.checklist(_title, choices=_choices) | |
_rc = code == self.d.OK | |
if code == self.d.OK: | |
self.outdic[_key] = tag | |
elif _dialog.lower() == 'menu': | |
_choices = mi.get('choices',[]) | |
for _c in _choices: | |
if _c[0] == _val: | |
_c[1] += DiagPy.SELECTED | |
elif _c[1].endswith(DiagPy.SELECTED): | |
_c[1] = _c[1][:-len(DiagPy.SELECTED)] | |
code, tag = self.d.menu(_title, choices=_choices) | |
_rc = code == self.d.OK | |
if code == self.d.OK: | |
self.outdic[_key] = tag[0] | |
elif _dialog.lower() == 'passwordbox': | |
code, tag = self.d.passwordbox(_title) | |
if code == self.d.OK: | |
code, tag2 = self.d.passwordbox('%s [confirm]' % _title) | |
if tag != tag2: | |
self.d.msgbox("Password not matched!") | |
code = self.d.CANCEL | |
else: | |
self.outdic[_key] = tag | |
_rc = code == self.d.OK | |
return _rc | |
#===================================================================================== | |
def execute(self): | |
postjob = self.dic.get('postjob',[]) | |
if not postjob: return False | |
pstep = 100 / len(postjob) | |
percent = 0 | |
for _job in postjob: | |
_name = _job.get('name','job...') | |
self.d.gauge_start(_name) | |
_cmd = _job.get('cmd', 'echo') | |
_key_parameters = _job.get('key-parameters',[]) | |
_after_stop = _job.get('after-stop', False) | |
_parameters = [] | |
for _kp in _key_parameters: | |
_val = self.outdic.get(_kp) | |
if _val is None: _val = '' | |
if isinstance(_val, unicode): | |
_val = _val.encode('utf8') | |
_parameters.append(str(_val)) | |
cmds = [ _cmd ] | |
cmds.extend(_parameters) | |
po = subprocess.Popen(cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
_stdout = po.stdout.read() | |
_stderr = po.stderr.read() | |
po.wait() | |
if _after_stop: | |
_text = 'Command: %s\n\nSTDOUT:\n%s\n\nSTDERR:\n%s\n\nReturn Code: %s' % \ | |
(' '.join(cmds), _stdout, _stderr, po.returncode) | |
with open('/tmp/foo.txt','w') as ofp: | |
ofp.write(_text) | |
ofp.write("\ntype(_text)=%s"%type(_text)) | |
#self.d.scrollbox(_text.encode('utf8'), title=_name) | |
self.d.scrollbox(_text.decode('utf8'), title=_name) | |
percent += pstep | |
self.d.gauge_update(percent, _name) # 10% of the whole task is done | |
time.sleep(0.1) | |
exit_code = self.d.gauge_stop() # cleanup actions | |
# self.d.gauge_update(100, "Done!") # work is done | |
# time.sleep(0.5) | |
#===================================================================================== | |
def do(self): | |
try: | |
self.intro() | |
_depth = 0 | |
_depth_list = [] | |
_head_list = [] | |
self.menu = self.getTopMenu() | |
_depth_list.append(self.menu) | |
_head_list.append(self._text) | |
while True: | |
code, tag = self.showMenu(self.menu, head=_head_list[_depth], depth=_depth) | |
if code == self.d.OK: | |
mi = self._menudic[tag[0]] | |
_menu = mi.get('menu', None) | |
self.showDialog(mi) | |
if _menu is not None: | |
self.menu = _menu | |
_depth += 1 | |
_depth_list.append(self.menu) | |
_head_list.append(mi.get('title')) | |
else: | |
if _depth == 0: | |
break | |
_depth -= 1 | |
_depth_list.pop() | |
_head_list.pop() | |
self.menu = _depth_list[_depth] | |
if code == self.d.EXTRA: | |
self._save() | |
self.execute() | |
self.bye() | |
except: | |
self.bye(traceback.format_exc()) | |
########################################################################################## | |
def d_res(d, code, *args): | |
if code == d.ESC: | |
d.msgbox("You got out of the menu by pressing the Escape key.") | |
else: | |
text = "code={}, args={}".format(code, args) | |
d.msgbox(text, width=40, height=10) | |
########################################################################################## | |
def test(): | |
# This is almost always a good thing to do at the beginning of your programs. | |
# locale.setlocale(locale.LC_ALL, 'UTF8') | |
d = Dialog(dialog="dialog") | |
button_names = {d.OK: "예", | |
d.CANCEL: u"아니오", | |
d.HELP: u"도움말", | |
d.EXTRA: u"Extra"} | |
code = d.yesno(u"<예/아니오>", height=10, width=40, help_button=True) | |
d_res(d,code) | |
code, answer = d.inputbox(u"<입력창>", init=u"초기값", help_button=True) | |
d_res(d,code, answer) | |
code, tag = d.menu(u"레이디오 선택", | |
choices=[(u"태그1", "Item text 1"), | |
("Tag 2", "Item text 2"), | |
("Tag 3", "Item text 3")]) | |
d_res(d,code, tag) | |
code, t = d.checklist( | |
u"체크선택", height=0, width=0, list_height=0, | |
choices=[ ("Tag 1", "Item 1", False), | |
("Tag 2", "Item 2", True), | |
(u"태그3", u"항목 3", True) ], | |
help_button=True) | |
d_res(d,code, t) | |
d.gauge_start(u'프로그래스 진행') | |
time.sleep(1) | |
# do something | |
d.gauge_update(10) # 10% of the whole task is done | |
time.sleep(1) | |
# ... | |
d.gauge_update(100, "any text here") # work is done | |
time.sleep(1) | |
exit_code = d.gauge_stop() # cleanup actions | |
time.sleep(1) | |
d.infobox("Bye bye...", width=0, height=0, title="This is the end") | |
time.sleep(1) | |
########################################################################################## | |
if __name__ == '__main__': | |
signal.signal(signal.SIGINT, signal_handler) | |
dp = DiagPy() | |
dp.do() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment