00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 import sys
00032 import os
00033 import time
00034
00035 from Tkinter import *
00036 from Tkconstants import *
00037 from tkFileDialog import *
00038 import tkFont
00039
00040 import xml.parsers.expat as expat
00041
00042 from hekateros_control.Utils import *
00043
00044
00045
00046
00047
00048
00049
00050
00051 class ConfigDlg(Toplevel):
00052
00053 Home = os.path.expanduser("~")
00054
00055
00056 UserDirName = ".roadnarrows"
00057
00058
00059 ConfigFileName = "hek_panel.xml"
00060
00061 PathNameDft = Home + os.path.sep + \
00062 UserDirName + os.path.sep + \
00063 ConfigFileName
00064
00065
00066 ConfigDft = \
00067 {
00068 'warn_on_calib': True,
00069 'warn_on_release': True,
00070 'force_recalib': True,
00071 }
00072
00073
00074
00075
00076
00077
00078
00079 def __init__(self, master=None, cnf={}, **kw):
00080
00081
00082 kw = self.initData(kw)
00083
00084 Toplevel.__init__(self, master=master, cnf=cnf, **kw)
00085
00086 self.title("hek_panel configuration")
00087
00088
00089 self.createWidgets()
00090
00091
00092 self.m_bttnCancel.bind('<KeyPress-Return>', func=self.close)
00093
00094
00095 if master is not None:
00096 self.update_idletasks()
00097 x0 = master.winfo_rootx()
00098 y0 = master.winfo_rooty()
00099 xp = x0 + (master.winfo_width() - self.winfo_width()) / 2 - 8
00100 yp = y0 + (master.winfo_height() - self.winfo_height()) / 2 - 20
00101 glist = [self.winfo_width(), self.winfo_height(), xp, yp]
00102
00103 self.geometry('{0}x{1}+{2}+{3}'.format(*glist))
00104
00105
00106
00107 self.protocol("WM_DELETE_WINDOW", self.close)
00108
00109
00110
00111
00112
00113 self.focus_set()
00114
00115
00116 self.grab_set()
00117
00118
00119 self.transient(master)
00120
00121
00122 self.wait_window(self)
00123
00124
00125
00126
00127
00128
00129
00130
00131 def initData(self, kw):
00132 self.m_saved = False
00133 self.m_filename = None
00134 if kw.has_key('config'):
00135 self.m_config = kw['config']
00136 del kw['config']
00137 else:
00138 self.m_config = ConfigDft;
00139 return kw
00140
00141
00142
00143
00144 def createWidgets(self):
00145 frame = Frame(self)
00146 frame.grid(row=0, column=0)
00147
00148
00149 w = Label(frame)
00150 helv = tkFont.Font(family="Helvetica",size=24,weight="bold")
00151 w['font'] = helv
00152 w['text'] = 'Configuration'
00153 w['anchor'] = CENTER
00154 w.grid(row=0, column=0, sticky=E+W)
00155
00156
00157 row = 1
00158 self.m_varWarnCalib = IntVar()
00159 self.m_varWarnCalib.set(self.m_config['warn_on_calib'])
00160 w = Checkbutton(frame,
00161 text="Show warning dialog before calibrating arm.",
00162 variable=self.m_varWarnCalib)
00163 w.grid(row=row, column=0, padx=5, sticky=W)
00164
00165
00166 row += 1
00167 self.m_varForceRecalib = IntVar()
00168 self.m_varForceRecalib.set(self.m_config['force_recalib'])
00169 w = Checkbutton(frame,
00170 text="Force (re)calibration for all joints on calibrate action.",
00171 variable=self.m_varForceRecalib)
00172 w.grid(row=row, column=0, padx=20, sticky=W)
00173
00174 row += 1
00175 spacer = Label(frame, text=" ");
00176 spacer.grid(row=row, column=0, padx=5, sticky=W)
00177
00178
00179 row += 1
00180 self.m_varWarnRelease = IntVar()
00181 self.m_varWarnRelease.set(self.m_config['warn_on_release'])
00182 w = Checkbutton(frame,
00183 text="Show warning dialog before releasing arm.",
00184 variable=self.m_varWarnRelease)
00185 w.grid(row=row, column=0, padx=5, sticky=W)
00186
00187 row += 1
00188 spacer = Label(frame, text=" ");
00189 spacer.grid(row=row, column=0, padx=5, sticky=W)
00190
00191
00192
00193
00194 row += 1
00195 wframe = Frame(frame)
00196 wframe.grid(row=row, column=0)
00197
00198
00199 w = Button(wframe, width=10, text='Cancel', command=self.close)
00200 w.grid(row=0, column=0, padx=2, pady=5)
00201 w['anchor'] = CENTER
00202 self.m_bttnCancel = w
00203
00204
00205 w = Button(wframe, width=10, text='Save', command=self.ok)
00206 w.grid(row=0, column=1, padx=2, pady=5)
00207 w['anchor'] = CENTER
00208 self.m_bttnOk = w
00209
00210
00211
00212
00213 def ok(self):
00214 if self.m_varWarnCalib.get():
00215 self.m_config['warn_on_calib'] = True
00216 else:
00217 self.m_config['warn_on_calib'] = False
00218 if self.m_varForceRecalib.get():
00219 self.m_config['force_recalib'] = True
00220 else:
00221 self.m_config['force_recalib'] = False
00222 if self.m_varWarnRelease.get():
00223 self.m_config['warn_on_release'] = True
00224 else:
00225 self.m_config['warn_on_release'] = False
00226 dirname = ConfigDlg.Home + os.path.sep + ConfigDlg.UserDirName
00227 if not os.path.isdir(dirname):
00228 try:
00229 os.mkdir(dirname, 0755)
00230 except OSError, err:
00231 print "%s: %s" % (dirname, err)
00232 return
00233 self.m_filename = dirname + os.path.sep + ConfigDlg.ConfigFileName
00234 xml = ConfigXml()
00235 xml.save(self.m_filename, self.m_config)
00236 self.close()
00237 self.m_saved = True
00238
00239
00240
00241
00242 def close(self):
00243 self.destroy()
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 class ConfigXml():
00254 def __init__(self):
00255 self.m_curData = ""
00256 self.m_config = None
00257
00258 def parse(self, pathname=None):
00259 if pathname is None:
00260 pathname = ConfigDlg.PathNameDft;
00261 self.m_config = ConfigDlg.ConfigDft
00262 try:
00263 fp = open(pathname, 'r')
00264 except IOError, err:
00265 return self.m_config
00266 parser = expat.ParserCreate()
00267 parser.returns_unicode = False
00268 parser.StartElementHandler = self.onElemStart
00269 parser.CharacterDataHandler = self.onElemData
00270 parser.EndElementHandler = self.onElemEnd
00271 self.m_stack = []
00272 try:
00273 parser.ParseFile(fp)
00274 except expat.ExpatError as e:
00275 print "%s: %s" % (pathname, expat.ErrorString(e.code))
00276 fp.close()
00277 return self.m_config
00278
00279 def save(self, pathname, config):
00280 try:
00281 fp = open(pathname, 'w')
00282 except IOError, err:
00283 print "%s: %s." % (pathname, err)
00284 return
00285 fp.write("""\
00286 <?xml version="1.0" encoding="utf-8"?>
00287 <?xml-stylesheet type="text/xsl" href="http://roadnarrows.com/xml/Hekateros/1.0/hekateros.xsl"?>
00288
00289 <!-- RoadNarrows Hekateros Top-Level Configuration -->
00290 <hekateros xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
00291 xsi:noNamespaceSchemaLocation="http://roadnarrows.com/xml/Hekateros/1.0/hekateros.xsd">
00292
00293 <!-- hek_panel configuration -->
00294 <hek_panel>
00295 """)
00296
00297 self.writeTree(fp, 4, config);
00298
00299 fp.write("""\
00300 </hek_panel>
00301
00302 </hekateros>
00303 """)
00304
00305 fp.close()
00306
00307 def writeTree(self, fp, indent, config):
00308 s = ' ' * indent
00309 for key,data in config.iteritems():
00310 if type(data) is dict:
00311 fp.write("{0}<{1}>\n".format(s, key))
00312 self.writeTree(fp, indent+2, data)
00313 fp.write("{0}</{1}>\n".format(s, key))
00314 else:
00315 fp.write("{0}<{1}>{2}</{3}>\n".format(s, key, config[key], key))
00316
00317 def onElemStart(self, elem, attrs):
00318
00319 self.m_stack.append(elem)
00320
00321 self.m_curData = ""
00322
00323 def onElemData(self, data):
00324
00325 self.m_curData += data
00326
00327 def onElemEnd(self, elem):
00328
00329
00330 if len(self.m_stack) == 3:
00331 elem = self.m_stack[2]
00332 if self.m_config.has_key(elem):
00333 if elem in ['warn_on_calib', 'force_recalib', 'warn_on_release']:
00334 self.m_config[elem] = self.cvtToBool(self.m_curData.strip())
00335 try:
00336 self.m_stack.pop()
00337 except:
00338 pass
00339
00340 self.m_curData = ""
00341
00342 def cvtToBool(self, data):
00343 if data.lower() == 'true':
00344 return True
00345 elif data.lower() == 'false':
00346 return False
00347 else:
00348 try:
00349 i = int(data)
00350 if i:
00351 return True
00352 else:
00353 return False
00354 except ValueError:
00355 return False
00356
00357 def cvtToFloat(self, data):
00358 try:
00359 return float(data)
00360 except (TypeError, ValueError):
00361 return 0.0