Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@mcchae
Created June 17, 2015 07:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mcchae/709063c21812ba637620 to your computer and use it in GitHub Desktop.
Save mcchae/709063c21812ba637620 to your computer and use it in GitHub Desktop.
diagpy : Simple Python dialog based Text UI
#!/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
{ # 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,
}
}
#!/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