1#!/usr/local/bin/python3.8 2############################################################################ 3# 4# wxgui.py 5# 6# Copyright 2004 Donour Sizemore (donour@uchicago.edu) 7# Copyright 2009 Secons Ltd. (www.obdtester.com) 8# 9# This file is part of pyOBD. 10# 11# pyOBD is free software; you can redistribute it and/or modify 12# it under the terms of the GNU General Public License as published by 13# the Free Software Foundation; either version 2 of the License, or 14# (at your option) any later version. 15# 16# pyOBD is distributed in the hope that it will be useful, 17# but WITHOUT ANY WARRANTY; without even the implied warranty of 18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19# GNU General Public License for more details. 20# 21# You should have received a copy of the GNU General Public License 22# along with pyOBD; if not, write to the Free Software 23# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24############################################################################ 25 26#import wxversion 27#wxversion.select("2.6") 28import wx 29 30import obd_io #OBD2 funcs 31import os #os.environ 32 33import threading 34import sys 35import serial 36import platform 37import time 38import ConfigParser #safe application configuration 39import webbrowser #open browser from python 40 41from obd2_codes import pcodes 42from obd2_codes import ptest 43 44from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin 45 46ID_ABOUT = 101 47ID_EXIT = 110 48ID_CONFIG = 500 49ID_CLEAR = 501 50ID_GETC = 502 51ID_RESET = 503 52ID_LOOK = 504 53ALL_ON = 505 54ALL_OFF = 506 55 56ID_DISCONNECT = 507 57ID_HELP_ABOUT = 508 58ID_HELP_VISIT = 509 59ID_HELP_ORDER = 510 60 61# Define notification event for sensor result window 62EVT_RESULT_ID = 1000 63def EVT_RESULT(win, func,id): 64 """Define Result Event.""" 65 win.Connect(-1, -1, id, func) 66 67#event pro akutalizaci Trace tabu 68class ResultEvent(wx.PyEvent): 69 """Simple event to carry arbitrary result data.""" 70 def __init__(self, data): 71 """Init Result Event.""" 72 wx.PyEvent.__init__(self) 73 self.SetEventType(EVT_RESULT_ID) 74 self.data = data 75 76#event pro aktualizaci DTC tabu 77EVT_DTC_ID = 1001 78class DTCEvent(wx.PyEvent): 79 """Simple event to carry arbitrary result data.""" 80 def __init__(self, data): 81 """Init Result Event.""" 82 wx.PyEvent.__init__(self) 83 self.SetEventType(EVT_DTC_ID) 84 self.data = data 85 86#event pro aktualizaci status tabu 87EVT_STATUS_ID = 1002 88class StatusEvent(wx.PyEvent): 89 """Simple event to carry arbitrary result data.""" 90 def __init__(self, data): 91 """Init Result Event.""" 92 wx.PyEvent.__init__(self) 93 self.SetEventType(EVT_STATUS_ID) 94 self.data = data 95 96#event pro aktualizaci tests tabu 97EVT_TESTS_ID = 1003 98class TestEvent(wx.PyEvent): 99 """Simple event to carry arbitrary result data.""" 100 def __init__(self, data): 101 """Init Result Event.""" 102 wx.PyEvent.__init__(self) 103 self.SetEventType(EVT_TESTS_ID) 104 self.data = data 105 106#defines notification event for debug tracewindow 107from debugEvent import * 108 109class MyApp(wx.App): 110 # A listctrl which auto-resizes the column boxes to fill 111 class MyListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin): 112 def __init__(self, parent, id, pos = wx.DefaultPosition, 113 size = wx.DefaultSize, style = 0): 114 wx.ListCtrl.__init__(self,parent,id,pos,size,style) 115 ListCtrlAutoWidthMixin.__init__(self) 116 117 class sensorProducer(threading.Thread): 118 def __init__(self, _notify_window,portName,SERTIMEOUT,RECONNATTEMPTS,_nb): 119 from Queue import Queue 120 self.portName = portName 121 self.RECONNATTEMPTS=RECONNATTEMPTS 122 self.SERTIMEOUT=SERTIMEOUT 123 self.port = None 124 self._notify_window=_notify_window 125 self._nb=_nb 126 threading.Thread.__init__ ( self ) 127 128 def initCommunication(self): 129 self.port = obd_io.OBDPort(self.portName,self._notify_window,self.SERTIMEOUT,self.RECONNATTEMPTS) 130 131 if self.port.State==0: #Cant open serial port 132 return None 133 134 self.active = [] 135 self.supp = self.port.sensor(0)[1] #read supported PIDS 136 137 self.active.append(1); #PID 0 is always supported 138 139 wx.PostEvent(self._notify_window, ResultEvent([0,0,"X"])) 140 wx.PostEvent(self._notify_window, DebugEvent([1,"Communication initialized..."])) 141 142 for i in range(1, len(self.supp)): 143 if self.supp[i-1] == "1": #put X in coloum if PID is supported 144 self.active.append(1) 145 wx.PostEvent(self._notify_window, ResultEvent([i,0,"X"])) 146 else: 147 self.active.append(0) 148 wx.PostEvent(self._notify_window, ResultEvent([i,0,""])) 149 return "OK" 150 151 def run(self): 152 wx.PostEvent(self._notify_window, StatusEvent([0,1,"Connecting...."])) 153 self.initCommunication() 154 if self.port.State==0: #cant connect, exit thread 155 self.stop() 156 wx.PostEvent(self._notify_window, StatusEvent([666])) #signal apl, that communication was disconnected 157 wx.PostEvent(self._notify_window, StatusEvent([0,1,"Error cant connect..."])) 158 return None 159 160 wx.PostEvent(self._notify_window, StatusEvent([0,1,"Connected"])) 161 wx.PostEvent(self._notify_window, StatusEvent([2,1,self.port.ELMver])) 162 prevstate=-1 163 curstate=-1 164 while self._notify_window.ThreadControl!=666: 165 prevstate=curstate 166 curstate=self._nb.GetSelection() 167 if curstate==0: #show status tab 168 pass 169 elif curstate==1: #show tests tab 170 res=self.port.get_tests_MIL() 171 for i in range(0,len(res)): 172 wx.PostEvent(self._notify_window, TestEvent([i,1,res[i]])) 173 174 elif curstate==2: #show sensor tab 175 for i in range(3, len(self.active)): 176 if self.active[i]: 177 s = self.port.sensor(i) 178 wx.PostEvent(self._notify_window, ResultEvent([i,2,"%s (%s)" % (s[1], s[2])])) 179 if self._notify_window.ThreadControl==666: 180 break 181 elif curstate==3: #show DTC tab 182 if self._notify_window.ThreadControl == 1: #clear DTC 183 self.port.clear_dtc() 184 185 if self._notify_window.ThreadControl==666: #before reset ThreadControl we must check if main thread did not want us to finish 186 break 187 188 self._notify_window.ThreadControl=0 189 prevstate=-1 # to reread DTC 190 if self._notify_window.ThreadControl == 2: #reread DTC 191 prevstate=-1 192 193 if self._notify_window.ThreadControl==666: 194 break 195 196 self._notify_window.ThreadControl=0 197 if prevstate!=3: 198 wx.PostEvent(self._notify_window, DTCEvent(0)) #clear list 199 DTCCodes=self.port.get_dtc() 200 if len(DTCCodes)==0: 201 wx.PostEvent(self._notify_window, DTCEvent(["","","No DTC codes (codes cleared)"])) 202 for i in range (0,len(DTCCodes)): 203 wx.PostEvent(self._notify_window, DTCEvent([DTCCodes[i][1],DTCCodes[i][0],pcodes[DTCCodes[i][1]]])) 204 else: 205 pass 206 self.stop() 207 208 def off(self, id): 209 if id >= 0 and id < len(self.active): 210 self.active[id] = 0 211 else: 212 debug("Invalid sensor id") 213 def on(self, id): 214 if id >= 0 and id < len(self.active): 215 self.active[id] = 1 216 else: 217 debug("Invalid sensor id") 218 219 def all_off(self): 220 for i in range(0, len(self.active)): 221 self.off(i) 222 def all_on(self): 223 for i in range(0, len(self.active)): 224 self.off(i) 225 226 def stop(self): 227 if self.port != None: #if stop is called before any connection port is not defined (and not connected ) 228 self.port.close() 229 wx.PostEvent(self._notify_window, StatusEvent([0,1,"Disconnected"])) 230 wx.PostEvent(self._notify_window, StatusEvent([2,1,"----"])) 231 232 #class producer end 233 234 def sensor_control_on(self): #after connection enable few buttons 235 self.settingmenu.Enable(ID_CONFIG,False) 236 self.settingmenu.Enable(ID_RESET,False) 237 self.settingmenu.Enable(ID_DISCONNECT,True) 238 self.dtcmenu.Enable(ID_GETC,True) 239 self.dtcmenu.Enable(ID_CLEAR,True) 240 self.GetDTCButton.Enable(True) 241 self.ClearDTCButton.Enable(True) 242 243 def sensor_toggle(e): 244 sel = e.m_itemIndex 245 state = self.senprod.active[sel] 246 print sel, state 247 if state == 0: 248 self.senprod.on(sel) 249 self.sensors.SetStringItem(sel,1,"1") 250 elif state == 1: 251 self.senprod.off(sel) 252 self.sensors.SetStringItem(sel,1,"0") 253 else: 254 debug("Incorrect sensor state") 255 256 self.sensors.Bind(wx.EVT_LIST_ITEM_ACTIVATED,sensor_toggle,id=self.sensor_id) 257 258 def sensor_control_off(self): #after disconnect disable fer buttons 259 self.dtcmenu.Enable(ID_GETC,False) 260 self.dtcmenu.Enable(ID_CLEAR,False) 261 self.settingmenu.Enable(ID_DISCONNECT,False) 262 self.settingmenu.Enable(ID_CONFIG,True) 263 self.settingmenu.Enable(ID_RESET,True) 264 self.GetDTCButton.Enable(False) 265 self.ClearDTCButton.Enable(False) 266 #http://pyserial.sourceforge.net/ empty function 267 #EVT_LIST_ITEM_ACTIVATED(self.sensors,self.sensor_id, lambda : None) 268 269 def build_sensor_page(self): 270 HOFFSET_LIST=0 271 tID = wx.NewId() 272 self.sensor_id = tID 273 panel = wx.Panel(self.nb, -1) 274 275 self.sensors = self.MyListCtrl(panel, tID, pos=wx.Point(0,HOFFSET_LIST), 276 style= 277 wx.LC_REPORT | 278 wx.SUNKEN_BORDER | 279 wx.LC_HRULES | 280 wx.LC_SINGLE_SEL) 281 282 283 self.sensors.InsertColumn(0, "Supported",width=70) 284 self.sensors.InsertColumn(1, "Sensor",format=wx.LIST_FORMAT_RIGHT, width=250) 285 self.sensors.InsertColumn(2, "Value") 286 for i in range(0, len(obd_io.obd_sensors.SENSORS)): 287 s = obd_io.obd_sensors.SENSORS[i].name 288 self.sensors.InsertStringItem(i, "") 289 self.sensors.SetStringItem(i, 1, s) 290 291 292 #################################################################### 293 # This little bit of magic keeps the list the same size as the frame 294 def OnPSize(e, win = panel): 295 panel.SetSize(e.GetSize()) 296 self.sensors.SetSize(e.GetSize()) 297 w,h = self.frame.GetClientSizeTuple() 298 self.sensors.SetDimensions(0,HOFFSET_LIST, w-10 , h - 35 ) 299 300 panel.Bind(wx.EVT_SIZE,OnPSize) 301 #################################################################### 302 303 self.nb.AddPage(panel, "Sensors") 304 305 def build_DTC_page(self): 306 HOFFSET_LIST=30 #offset from the top of panel (space for buttons) 307 tID = wx.NewId() 308 self.DTCpanel = wx.Panel(self.nb, -1) 309 self.GetDTCButton = wx.Button(self.DTCpanel,-1 ,"Get DTC" , wx.Point(15,0)) 310 self.ClearDTCButton = wx.Button(self.DTCpanel,-1,"Clear DTC", wx.Point(100,0)) 311 312 #bind functions to button click action 313 self.DTCpanel.Bind(wx.EVT_BUTTON,self.GetDTC,self.GetDTCButton) 314 self.DTCpanel.Bind(wx.EVT_BUTTON,self.QueryClear,self.ClearDTCButton) 315 316 self.dtc = self.MyListCtrl(self.DTCpanel,tID, pos=wx.Point(0,HOFFSET_LIST), 317 style=wx.LC_REPORT|wx.SUNKEN_BORDER|wx.LC_HRULES|wx.LC_SINGLE_SEL) 318 319 self.dtc.InsertColumn(0, "Code", width=100) 320 self.dtc.InsertColumn(1, "Status",width=100) 321 self.dtc.InsertColumn(2, "Trouble code") 322 #################################################################### 323 # This little bit of magic keeps the list the same size as the frame 324 def OnPSize(e, win = self.DTCpanel): 325 self.DTCpanel.SetSize(e.GetSize()) 326 self.dtc.SetSize(e.GetSize()) 327 w,h = self.frame.GetClientSizeTuple() 328 # I have no idea where 70 comes from 329 self.dtc.SetDimensions(0,HOFFSET_LIST, w-16 , h - 70 ) 330 331 self.DTCpanel.Bind(wx.EVT_SIZE,OnPSize) 332 #################################################################### 333 334 self.nb.AddPage(self.DTCpanel, "DTC") 335 336 def TraceDebug(self,level,msg): 337 if self.DEBUGLEVEL<=level: 338 self.trace.Append([str(level),msg]) 339 340 def OnInit(self): 341 self.ThreadControl = 0 #say thread what to do 342 self.COMPORT = 0 343 self.senprod = None 344 self.DEBUGLEVEL = 0 #debug everthing 345 346 tID = wx.NewId() 347 348 #read settings from file 349 self.config = ConfigParser.RawConfigParser() 350 351 #print platform.system() 352 #print platform.mac_ver()[] 353 354 if "OS" in os.environ.keys(): #runnig under windows 355 self.configfilepath="pyobd.ini" 356 else: 357 self.configfilepath=os.environ['HOME']+'/.pyobdrc' 358 if self.config.read(self.configfilepath)==[]: 359 self.COMPORT="/dev/ttyU0" 360 self.RECONNATTEMPTS=5 361 self.SERTIMEOUT=2 362 else: 363 self.COMPORT=self.config.get("pyOBD","COMPORT") 364 self.RECONNATTEMPTS=self.config.getint("pyOBD","RECONNATTEMPTS") 365 self.SERTIMEOUT=self.config.getint("pyOBD","SERTIMEOUT") 366 367 frame = wx.Frame(None, -1, "pyOBD-II") 368 self.frame=frame 369 370 EVT_RESULT(self,self.OnResult,EVT_RESULT_ID) 371 EVT_RESULT(self,self.OnDebug, EVT_DEBUG_ID) 372 EVT_RESULT(self,self.OnDtc,EVT_DTC_ID) 373 EVT_RESULT(self,self.OnStatus,EVT_STATUS_ID) 374 EVT_RESULT(self,self.OnTests,EVT_TESTS_ID) 375 376 # Main notebook frames 377 self.nb = wx.Notebook(frame, -1, style = wx.NB_TOP) 378 379 self.status = self.MyListCtrl(self.nb, tID,style=wx.LC_REPORT|wx.SUNKEN_BORDER) 380 self.status.InsertColumn(0, "Description",width=200) 381 self.status.InsertColumn(1, "Value") 382 self.status.Append(["Link State","Disconnnected"]); 383 self.status.Append(["Protocol","---"]); 384 self.status.Append(["Cable version","---"]); 385 self.status.Append(["COM port",self.COMPORT]); 386 387 self.nb.AddPage(self.status, "Status") 388 389 self.OBDTests = self.MyListCtrl(self.nb, tID,style=wx.LC_REPORT|wx.SUNKEN_BORDER) 390 self.OBDTests.InsertColumn(0, "Description",width=200) 391 self.OBDTests.InsertColumn(1, "Value") 392 self.nb.AddPage(self.OBDTests, "Tests") 393 394 for i in range(0,len(ptest)): #fill MODE 1 PID 1 test description 395 self.OBDTests.Append([ptest[i],"---"]); 396 397 self.build_sensor_page() 398 399 self.build_DTC_page() 400 401 self.trace = self.MyListCtrl(self.nb, tID,style=wx.LC_REPORT|wx.SUNKEN_BORDER) 402 self.trace.InsertColumn(0, "Level",width=40) 403 self.trace.InsertColumn(1, "Message") 404 self.nb.AddPage(self.trace, "Trace") 405 self.TraceDebug(1,"Application started") 406 407 # Setting up the menu. 408 self.filemenu= wx.Menu() 409 self.filemenu.Append(ID_EXIT,"E&xit"," Terminate the program") 410 411 self.settingmenu = wx.Menu() 412 self.settingmenu.Append(ID_CONFIG,"Configure"," Configure pyOBD") 413 self.settingmenu.Append(ID_RESET,"Connect"," Reopen and connect to device") 414 self.settingmenu.Append(ID_DISCONNECT,"Disconnect","Close connection to device") 415 416 self.dtcmenu= wx.Menu() 417 # tady toto nastavi automaticky tab DTC a provede akci 418 self.dtcmenu.Append(ID_GETC ,"Get DTCs", " Get DTC Codes") 419 self.dtcmenu.Append(ID_CLEAR ,"Clear DTC", " Clear DTC Codes") 420 self.dtcmenu.Append(ID_LOOK ,"Code Lookup"," Lookup DTC Codes") 421 422 self.helpmenu = wx.Menu() 423 424 self.helpmenu.Append(ID_HELP_ABOUT ,"About this program", " Get DTC Codes") 425 self.helpmenu.Append(ID_HELP_VISIT ,"Visit program homepage"," Lookup DTC Codes") 426 self.helpmenu.Append(ID_HELP_ORDER ,"Order OBD-II interface", " Clear DTC Codes") 427 428 429 # Creating the menubar. 430 self.menuBar = wx.MenuBar() 431 self.menuBar.Append(self.filemenu,"&File") # Adding the "filemenu" to the MenuBar 432 self.menuBar.Append(self.settingmenu,"&OBD-II") 433 self.menuBar.Append(self.dtcmenu,"&Trouble codes") 434 self.menuBar.Append(self.helpmenu,"&Help") 435 436 frame.SetMenuBar(self.menuBar) # Adding the MenuBar to the Frame content. 437 438 frame.Bind(wx.EVT_MENU,self.OnExit,id=ID_EXIT)# attach the menu-event ID_EXIT to the 439 frame.Bind(wx.EVT_MENU,self.QueryClear,id=ID_CLEAR) 440 frame.Bind(wx.EVT_MENU,self.Configure,id=ID_CONFIG) 441 frame.Bind(wx.EVT_MENU,self.OpenPort,id=ID_RESET) 442 frame.Bind(wx.EVT_MENU,self.OnDisconnect,id=ID_DISCONNECT) 443 frame.Bind(wx.EVT_MENU,self.GetDTC,id=ID_GETC) 444 frame.Bind(wx.EVT_MENU,self.CodeLookup,id=ID_LOOK) 445 frame.Bind(wx.EVT_MENU,self.OnHelpAbout,id=ID_HELP_ABOUT) 446 frame.Bind(wx.EVT_MENU,self.OnHelpVisit,id=ID_HELP_VISIT) 447 frame.Bind(wx.EVT_MENU,self.OnHelpOrder,id=ID_HELP_ORDER) 448 449 self.SetTopWindow(frame) 450 451 frame.Show(True) 452 frame.SetSize((520,400)) 453 self.sensor_control_off() 454 455 return True 456 457 def OnHelpVisit(self,event): 458 webbrowser.open("http://www.obdtester.com/pyobd") 459 460 def OnHelpOrder(self,event): 461 webbrowser.open("http://www.obdtester.com/order") 462 463 def OnHelpAbout(self,event): #todo about box 464 Text = """ PyOBD is an automotive OBD2 diagnosting application using ELM237 cable. 465 466(C) 2008-2009 SeCons Ltd. 467(C) 2004 Charles Donour Sizemore 468 469http://www.obdtester.com/ 470http://www.secons.com/ 471 472 PyOBD is free software; you can redistribute it and/or modify 473it under the terms of the GNU General Public License as published by the Free Software Foundation; 474either version 2 of the License, or (at your option) any later version. 475 476 PyOBD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 477without even the implied warranty of MEHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 478See the GNU General Public License for more details. You should have received a copy of 479the GNU General Public License along with PyOBD; if not, write to 480the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 481""" 482 483 #HelpAboutDlg = wx.Dialog(self.frame, id, title="About") 484 485 486 #box = wx.BoxSizer(wx.HORIZONTAL) 487 #box.Add(wx.StaticText(reconnectPanel,-1,Text,pos=(0,0),size=(200,200))) 488 #box.Add(wx.Button(HelpAboutDlg,wx.ID_OK),0) 489 #box.Add(wx.Button(HelpAboutDlg,wx.ID_CANCEL),1) 490 491 #HelpAboutDlg.SetSizer(box) 492 #HelpAboutDlg.SetAutoLayout(True) 493 #sizer.Fit(HelpAboutDlg) 494 #HelpAboutDlg.ShowModal() 495 496 self.HelpAboutDlg = wx.MessageDialog(self.frame, Text, 'About',wx.OK | wx.ICON_INFORMATION) 497 self.HelpAboutDlg.ShowModal() 498 self.HelpAboutDlg.Destroy() 499 500 def OnResult(self,event): 501 self.sensors.SetStringItem(event.data[0], event.data[1], event.data[2]) 502 503 def OnStatus(self,event): 504 if event.data[0] == 666: #signal, that connection falied 505 self.sensor_control_off() 506 else: 507 self.status.SetStringItem(event.data[0], event.data[1], event.data[2]) 508 509 def OnTests(self,event): 510 self.OBDTests.SetStringItem(event.data[0], event.data[1], event.data[2]) 511 512 def OnDebug(self,event): 513 self.TraceDebug(event.data[0],event.data[1]) 514 515 def OnDtc(self,event): 516 if event.data == 0: #signal, that DTC was cleared 517 self.dtc.DeleteAllItems() 518 else: 519 self.dtc.Append(event.data) 520 521 def OnDisconnect(self,event): #disconnect connection to ECU 522 self.ThreadControl=666 523 self.sensor_control_off() 524 525 def OpenPort(self,e): 526 527 if self.senprod: # signal current producers to finish 528 self.senprod.stop() 529 self.ThreadControl = 0 530 self.senprod = self.sensorProducer(self,self.COMPORT,self.SERTIMEOUT,self.RECONNATTEMPTS,self.nb) 531 self.senprod.start() 532 533 self.sensor_control_on() 534 535 def GetDTC(self,e): 536 self.nb.SetSelection(3) 537 self.ThreadControl=2 538 539 def AddDTC(self, code): 540 self.dtc.InsertStringItem(0, "") 541 self.dtc.SetStringItem(0, 0, code[0]) 542 self.dtc.SetStringItem(0, 1, code[1]) 543 544 545 def CodeLookup(self,e = None): 546 id = 0 547 diag = wx.Frame(None, id, title="Diagnostic Trouble Codes") 548 549 tree = wx.TreeCtrl(diag, id, style = wx.TR_HAS_BUTTONS) 550 551 root = tree.AddRoot("Code Reference") 552 proot = root; # tree.AppendItem(root,"Powertrain (P) Codes") 553 codes = pcodes.keys() 554 codes.sort() 555 group = "" 556 for c in codes: 557 if c[:3] != group: 558 group_root = tree.AppendItem(proot, c[:3]+"XX") 559 group = c[:3] 560 leaf = tree.AppendItem(group_root, c) 561 tree.AppendItem(leaf, pcodes[c]) 562 563 diag.SetSize((400,500)) 564 diag.Show(True) 565 566 567 def QueryClear(self,e): 568 id = 0 569 diag = wx.Dialog(self.frame, id, title="Clear DTC?") 570 571 sizer = wx.BoxSizer(wx.VERTICAL) 572 sizer.Add(wx.StaticText(diag, -1, "Are you sure you wish to"),0) 573 sizer.Add(wx.StaticText(diag, -1, "clear all DTC codes and "),0) 574 sizer.Add(wx.StaticText(diag, -1, "freeze frame data? "),0) 575 box = wx.BoxSizer(wx.HORIZONTAL) 576 box.Add(wx.Button(diag,wx.ID_OK, "Ok" ),0) 577 box.Add(wx.Button(diag,wx.ID_CANCEL, "Cancel"),0) 578 579 sizer.Add(box, 0) 580 diag.SetSizer(sizer) 581 diag.SetAutoLayout(True) 582 sizer.Fit(diag) 583 r = diag.ShowModal() 584 if r == wx.ID_OK: 585 self.ClearDTC() 586 587 def ClearDTC(self): 588 self.ThreadControl=1 589 self.nb.SetSelection(3) 590 591 592 def scanSerial(self): 593 """scan for available ports. return a list of serial names""" 594 available = [] 595 available.append("/dev/ttyU0") 596 597 # ELM-USB shows up as /dev/tty.usbmodemXXXX, where XXXX is a changing hex string 598 # on connection; so we have to search through all 64K options 599 if len(platform.mac_ver()[0])!=0: #search only on MAC 600 for i in range (65535): 601 extension = hex(i).replace("0x","", 1) 602 try: 603 s = serial.Serial("/dev/tty.usbmodem"+extension) 604 available.append(s.portstr) 605 s.close() 606 except serial.SerialException: 607 pass 608 609 return available 610 611 def Configure(self,e = None): 612 id = 0 613 diag = wx.Dialog(self.frame, id, title="Configure") 614 sizer = wx.BoxSizer(wx.VERTICAL) 615 616 ports = self.scanSerial() 617 rb = wx.RadioBox(diag, id, "Choose Serial Port", 618 choices = ports, style = wx.RA_SPECIFY_COLS, 619 majorDimension = 2) 620 621 sizer.Add(rb, 0) 622 623 #timeOut input control 624 timeoutPanel = wx.Panel(diag, -1) 625 timeoutCtrl = wx.TextCtrl(timeoutPanel, -1, '',pos=(140,0), size=(35, 25)) 626 timeoutStatic = wx.StaticText(timeoutPanel,-1,'Timeout:',pos=(3,5),size=(140,20)) 627 timeoutCtrl.SetValue(str(self.SERTIMEOUT)) 628 629 #reconnect attempt input control 630 reconnectPanel = wx.Panel(diag, -1) 631 reconnectCtrl = wx.TextCtrl(reconnectPanel, -1, '',pos=(140,0), size=(35, 25)) 632 reconnectStatic = wx.StaticText(reconnectPanel,-1,'Reconnect attempts:',pos=(3,5),size=(140,20)) 633 reconnectCtrl.SetValue(str(self.RECONNATTEMPTS)) 634 635 #web open link button 636 self.OpenLinkButton = wx.Button(diag,-1,"Click here to order ELM-USB interface",size=(260,30)) 637 diag.Bind(wx.EVT_BUTTON,self.OnHelpOrder,self.OpenLinkButton) 638 639 #set actual serial port choice 640 if (self.COMPORT != 0) and (self.COMPORT in ports): 641 rb.SetSelection(ports.index(self.COMPORT)) 642 643 644 sizer.Add(self.OpenLinkButton) 645 sizer.Add(timeoutPanel,0) 646 sizer.Add(reconnectPanel,0) 647 648 box = wx.BoxSizer(wx.HORIZONTAL) 649 box.Add(wx.Button(diag,wx.ID_OK),0) 650 box.Add(wx.Button(diag,wx.ID_CANCEL),1) 651 652 sizer.Add(box, 0) 653 diag.SetSizer(sizer) 654 diag.SetAutoLayout(True) 655 sizer.Fit(diag) 656 r = diag.ShowModal() 657 if r == wx.ID_OK: 658 659 #create section 660 if self.config.sections()==[]: 661 self.config.add_section("pyOBD") 662 #set and save COMPORT 663 self.COMPORT = ports[rb.GetSelection()] 664 self.config.set("pyOBD","COMPORT",self.COMPORT) 665 666 #set and save SERTIMEOUT 667 self.SERTIMEOUT = int(timeoutCtrl.GetValue()) 668 self.config.set("pyOBD","SERTIMEOUT",self.SERTIMEOUT) 669 self.status.SetStringItem(3,1,self.COMPORT); 670 671 #set and save RECONNATTEMPTS 672 self.RECONNATTEMPTS = int(reconnectCtrl.GetValue()) 673 self.config.set("pyOBD","RECONNATTEMPTS",self.RECONNATTEMPTS) 674 675 #write configuration to cfg file 676 self.config.write(open(self.configfilepath, 'wb')) 677 678 679 def OnExit(self,e = None): 680 import sys 681 sys.exit(0) 682 683app = MyApp(0) 684app.MainLoop() 685