DigitalWatchGUI.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. #
  2. # DigitalWatchGUI.py
  3. #
  4. # class DigitalWatchGUI provides a simple Tkinter GUI for a Digital Watch
  5. # to add: battery dies ... DONE OCT 2008 by Reehan Shaikh reehan.shaikh@cs.mcgill.ca
  6. # Updated for the SCCDXML compiler and AToMPM formalism by Yentl Van Tendeloo
  7. from Tkinter import *
  8. import tkFont
  9. from time import *
  10. from Tkinter import PhotoImage
  11. import python_runtime.statecharts_core as statecharts_core
  12. CANVAS_W = 222
  13. CANVAS_H = 236
  14. OVAL_X0=2
  15. OVAL_Y0=1
  16. OVAL_X1=CANVAS_W-OVAL_X0
  17. OVAL_Y1=CANVAS_H-OVAL_Y0
  18. RECT_X0=51
  19. RECT_Y0=95
  20. RECT_X1=CANVAS_W-RECT_X0+1
  21. RECT_Y1=CANVAS_H-RECT_Y0+10
  22. FONT_TIME=("terminal", 14)
  23. FONT_DATE=("terminal", 8)
  24. FONT_NOTE=("symbol", 10)
  25. class DigitalWatchGUI:
  26. def __init__(self, parent):
  27. self.controller = DigitalWatchGUI_Controller()
  28. self.staticGUI = DigitalWatchGUI_Static(parent, self.controller)
  29. self.dynamicGUI = DigitalWatchGUI_Dynamic(self.controller)
  30. class DigitalWatchGUI_Controller:
  31. def __init__(self):
  32. self.statechart = None
  33. self.GUI = None
  34. self.battery = True
  35. def bindDynamic(self, statechart):
  36. self.statechart = statechart
  37. def bindStatic(self, GUI):
  38. self.GUI = GUI
  39. #Interface for the GUI
  40. def window_close(self):
  41. import sys
  42. sys.exit(0)
  43. self.statechart.event('GUI Quit')
  44. def topRightPressed(self):
  45. self.statechart.event('topRightPressed')
  46. def topRightReleased(self):
  47. self.statechart.event('topRightReleased')
  48. def topLeftPressed(self):
  49. self.statechart.event('topLeftPressed')
  50. def topLeftReleased(self):
  51. self.statechart.event('topLeftReleased')
  52. def bottomRightPressed(self):
  53. self.statechart.event('bottomRightPressed')
  54. def bottomRightReleased(self):
  55. self.statechart.event('bottomRightReleased')
  56. def bottomLeftPressed(self):
  57. self.statechart.event('bottomLeftPressed')
  58. def bottomLeftReleased(self):
  59. self.statechart.event('bottomLeftReleased')
  60. def alarm(self):
  61. self.statechart.event('alarmStart')
  62. #Interface for the statechart
  63. #synchronize the state with the display:
  64. def batteryHalf(self):
  65. self.battery = False
  66. self.GUI.battery = False
  67. self.refreshDateDisplay()
  68. def batteryFull(self):
  69. self.battery = True
  70. self.GUI.battery = True
  71. self.refreshDateDisplay()
  72. def refreshTimeDisplay(self):
  73. self.GUI.drawTime()
  74. def refreshChronoDisplay(self):
  75. self.GUI.drawChrono()
  76. def refreshDateDisplay(self):
  77. self.GUI.drawDate()
  78. def refreshAlarmDisplay(self):
  79. self.GUI.drawAlarm()
  80. #Modify the state:
  81. def increaseTimeByOne(self):
  82. self.GUI.increaseTimeByOne()
  83. def resetChrono(self):
  84. self.GUI.resetChrono()
  85. def increaseChronoByOne(self):
  86. self.GUI.increaseChronoByOne()
  87. #Select current display:
  88. def startSelection(self):
  89. self.GUI.startSelection()
  90. def selectNext(self):
  91. self.GUI.selectNext()
  92. #Modify the state corresponing to the selection
  93. def increaseSelection(self):
  94. self.GUI.increaseSelection()
  95. def stopSelection(self):
  96. self.GUI.stopSelection()
  97. #Light / Alarm:
  98. def setIndiglo(self):
  99. self.GUI.setIndiglo()
  100. def unsetIndiglo(self):
  101. self.GUI.unsetIndiglo()
  102. def setAlarm(self):
  103. self.GUI.setAlarm()
  104. # Query
  105. def getTime(self):
  106. return self.GUI.getTime()
  107. def getAlarm(self):
  108. return self.GUI.getAlarm()
  109. #Check if time = alarm set time
  110. def checkTime(self):
  111. if self.GUI.getTime()[0] == self.GUI.getAlarm()[0] and self.GUI.getTime()[1] == self.GUI.getAlarm()[1] and self.GUI.getTime()[2] == self.GUI.getAlarm()[2]:
  112. self.alarm()
  113. return True
  114. else:
  115. return False
  116. class DigitalWatchGUI_Dynamic:
  117. def __init__(self, controller):
  118. self.controller = controller
  119. self.controller.bindDynamic(self)
  120. def setStatechart(self, controller):
  121. print("STATECHART set")
  122. self.controller_SCCD = controller
  123. def event(self, evt, param=[]):
  124. self.controller_SCCD.addInput(statecharts_core.Event(evt, "tkinter_input", param), 0.0)
  125. #======================================================================#
  126. # GUI Static class
  127. class DigitalWatchGUI_Static(Frame):
  128. def __init__(self, parent, controller):
  129. Frame.__init__(self, parent)
  130. self.parent = parent
  131. self.controller = controller
  132. self.controller.bindStatic(self)
  133. self.battery = True
  134. tmpDate = list(localtime()[0:3])
  135. self.curDate=[tmpDate[1],tmpDate[2],int(str(tmpDate[0])[3:]) ]
  136. self.dateTag=None
  137. self.curTime=list(localtime()[3:6])
  138. self.timeTag=None
  139. self.curAlarm=[12,0,0]
  140. self.alarmTag=None
  141. self.curChrono=[0,0,0]
  142. self.chronoTag=None
  143. self.noteImage=PhotoImage(file="./noteSmall.gif")
  144. self.watchImage=PhotoImage(file="./watch.gif")
  145. self.alarmNoteTag = None
  146. self.curSelectionInfo = None
  147. self.curSelection = ["hours", "minutes", "seconds",
  148. "months", "days", "years"]
  149. self.curSelectionIndex = 0
  150. self.lastPressed = ""
  151. self.createWidgets()
  152. #self.b_playpause.focus_force()
  153. parent.protocol("WM_DELETE_WINDOW", self.controller.window_close)
  154. self.drawTime()
  155. self.drawDate()
  156. def getTime(self):
  157. return self.curTime
  158. def getAlarm(self):
  159. return self.curAlarm
  160. def createWidgets(self):
  161. self.pack()
  162. self.displayCanvas = Canvas(master=self,
  163. takefocus=1,
  164. width=CANVAS_W, height=CANVAS_H,
  165. background="black")
  166. self.displayCanvas.pack(fill=BOTH, expand=1)
  167. self.displayCanvas.focus_set()
  168. self.watch = self.displayCanvas.create_image(0,0,image=self.watchImage,anchor="nw")
  169. self.display = self.displayCanvas.create_rectangle(RECT_X0,
  170. RECT_Y0,
  171. RECT_X1,
  172. RECT_Y1,
  173. fill="#DCDCDC")
  174. self.topRightButton = self.displayCanvas.create_rectangle(CANVAS_W-13,
  175. 60,
  176. CANVAS_W-3,
  177. 70,
  178. fill='')
  179. self.topLeftButton = self.displayCanvas.create_rectangle(3,
  180. 62,
  181. 13,
  182. 72,
  183. fill='')
  184. self.bottomRightButton = self.displayCanvas.create_rectangle(CANVAS_W-10,
  185. 162,
  186. CANVAS_W,
  187. 172,
  188. fill='')
  189. self.bottomLeftButton = self.displayCanvas.create_rectangle(3,
  190. 161,
  191. 13,
  192. 171,
  193. fill='')
  194. self.displayCanvas.bind("<ButtonPress-1>", self.mouse1Click)
  195. self.displayCanvas.bind("<ButtonRelease-1>", self.mouse1Release)
  196. def mouse1Click(self, event):
  197. X = self.displayCanvas.canvasx(event.x)
  198. Y = self.displayCanvas.canvasy(event.y)
  199. objTag = self.displayCanvas.find_closest(X,Y,halo=5)
  200. if self.topRightButton in objTag:
  201. self.controller.topRightPressed()
  202. self.lastPressed = "topRight"
  203. elif self.topLeftButton in objTag:
  204. self.controller.topLeftPressed()
  205. self.lastPressed = "topLeft"
  206. elif self.bottomLeftButton in objTag:
  207. self.controller.bottomLeftPressed()
  208. self.lastPressed = "bottomLeft"
  209. elif self.bottomRightButton in objTag:
  210. self.controller.bottomRightPressed()
  211. self.lastPressed = "bottomRight"
  212. else:
  213. self.lastPressed = ""
  214. def mouse1Release(self, event):
  215. if self.lastPressed == "topRight":
  216. self.controller.topRightReleased()
  217. elif self.lastPressed == "topLeft":
  218. self.controller.topLeftReleased()
  219. elif self.lastPressed == "bottomLeft":
  220. self.controller.bottomLeftReleased()
  221. elif self.lastPressed == "bottomRight":
  222. self.controller.bottomRightReleased()
  223. self.lastPressed=""
  224. def __intToString(self, i):
  225. if i<10:
  226. return "0"+str(i)
  227. else:
  228. return str(i)
  229. def __getTimeAsString(self):
  230. hours = self.__intToString(self.curTime[0])
  231. minutes = self.__intToString(self.curTime[1])
  232. seconds = self.__intToString(self.curTime[2])
  233. return hours+":"+minutes+":"+seconds
  234. def __getAlarmAsString(self):
  235. hours = self.__intToString(self.curAlarm[0])
  236. minutes = self.__intToString(self.curAlarm[1])
  237. seconds = self.__intToString(self.curAlarm[2])
  238. return hours+":"+minutes+":"+seconds
  239. def __getChronoAsString(self):
  240. minutes = self.__intToString(self.curChrono[0])
  241. seconds = self.__intToString(self.curChrono[1])
  242. centisecs = self.__intToString(self.curChrono[2])
  243. return minutes+":"+seconds+":"+centisecs
  244. def __getDateAsString(self):
  245. month = self.__intToString(self.curDate[0])
  246. day = self.__intToString(self.curDate[1])
  247. year = self.__intToString(self.curDate[2])
  248. return month+"/"+day+"/"+year
  249. def increaseHoursByOne(self):
  250. if self.timeTag != None:
  251. self.curTime[0]=(self.curTime[0]+1)%24
  252. else:
  253. self.curAlarm[0]=(self.curAlarm[0]+1)%24
  254. def increaseMinutesByOne(self):
  255. if self.timeTag != None:
  256. self.curTime[1]=(self.curTime[1]+1)%60
  257. else:
  258. self.curAlarm[1]=(self.curAlarm[1]+1)%60
  259. def increaseSecondsByOne(self):
  260. if self.timeTag != None:
  261. self.curTime[2]=(self.curTime[2]+1)%60
  262. else:
  263. self.curAlarm[2]=(self.curAlarm[2]+1)%60
  264. def increaseMonthsByOne(self):
  265. self.curDate[0]=(self.curDate[0]+1)%13
  266. if self.curDate[0]==0:
  267. self.curDate[0]=1
  268. def increaseDaysByOne(self):
  269. numDays = self.getNumDays()
  270. self.curDate[1]=(self.curDate[1]+1)%(numDays+1)
  271. if self.curDate[1]==0:
  272. self.curDate[1]=1
  273. def increaseYearsByOne(self):
  274. self.curDate[2]=(self.curDate[2]+1)%100
  275. if self.curDate[2]==0:
  276. self.curDate[2]=1
  277. def getNumDays(self):
  278. month = self.curDate[0]
  279. year = self.curDate[2]
  280. numDays=0
  281. if month==2: #february
  282. if year%4==0: #leap year
  283. numDays=29
  284. else:
  285. numDays=28
  286. else:
  287. if (month%2==1 and month<=7) or (month%2==0 and month>=8):
  288. numDays=31
  289. else:
  290. numDays=30
  291. return numDays
  292. def stopSelection(self):
  293. if self.curSelectionInfo != None:
  294. self.parent.after_cancel(self.curSelectionInfo[0])
  295. self.parent.after_cancel(self.curSelectionInfo[1])
  296. self.parent.after_cancel(self.curSelectionInfo[2])
  297. self.curSelectionInfo=None
  298. if self.timeTag != None:
  299. self.drawTime()
  300. self.drawDate()
  301. else:
  302. self.drawAlarm()
  303. def increaseSelection(self):
  304. self.stopSelection()
  305. selection = self.curSelection[self.curSelectionIndex]
  306. if selection == "hours":
  307. self.increaseHoursByOne()
  308. elif selection == "minutes":
  309. self.increaseMinutesByOne()
  310. elif selection == "seconds":
  311. self.increaseSecondsByOne()
  312. elif selection == "months":
  313. self.increaseMonthsByOne()
  314. elif selection == "days":
  315. self.increaseDaysByOne()
  316. elif selection == "years":
  317. self.increaseYearsByOne()
  318. if self.timeTag != None:
  319. self.drawTime()
  320. self.drawDate()
  321. else:
  322. self.drawAlarm()
  323. self.animateSelection()
  324. def selectNext(self):
  325. self.stopSelection()
  326. if self.timeTag != None:
  327. numDigits=len(self.curSelection)
  328. self.drawTime()
  329. self.drawDate()
  330. else:
  331. numDigits=3
  332. self.drawAlarm()
  333. self.curSelectionIndex=(self.curSelectionIndex+1)%numDigits
  334. self.animateSelection()
  335. def startSelection(self):
  336. self.curSelectionIndex=0
  337. self.animateSelection()
  338. def animateSelection(self):
  339. timeFunc=None
  340. if self.timeTag != None:
  341. timeFunc=self.drawTime
  342. else:
  343. timeFunc=self.drawAlarm
  344. curSelection = self.curSelection[self.curSelectionIndex]
  345. deleteEvent = None
  346. creationEvent = None
  347. if curSelection in ["hours", "minutes", "seconds"]:
  348. toDrawTime = ["hours", "minutes", "seconds"]
  349. toDrawTime.remove(curSelection)
  350. deleteEvent = self.parent.after(500, timeFunc, toDrawTime)
  351. creationEvent = self.parent.after(1000, timeFunc)
  352. else:
  353. toDrawDate = ["years", "months", "days"]
  354. toDrawDate.remove(curSelection)
  355. deleteEvent = self.parent.after(500, self.drawDate, toDrawDate)
  356. creationEvent = self.parent.after(1000, self.drawDate)
  357. animationEvent = self.parent.after(1000,self.animateSelection)
  358. self.curSelectionInfo = [deleteEvent,
  359. creationEvent,
  360. animationEvent]
  361. def increaseTimeByOne(self):
  362. self.curTime[2]=self.curTime[2]+1
  363. self.curTime[1]=(self.curTime[1]+self.curTime[2]/60)
  364. self.curTime[0]=(self.curTime[0]+self.curTime[1]/60)
  365. self.curTime[2]=self.curTime[2]%60
  366. self.curTime[1]=self.curTime[1]%60
  367. self.curTime[0]=self.curTime[0]%24
  368. if self.curTime[0]==0 and\
  369. self.curTime[1]==0 and\
  370. self.curTime[2]==0:
  371. self.increaseDateByOne()
  372. def increaseDateByOne(self):
  373. month = self.curDate[0]
  374. day = self.curDate[1]
  375. year = self.curDate[2]
  376. numMonths = 12
  377. numDays = self.getNumDays()
  378. self.curDate[1]=self.curDate[1]+1
  379. self.curDate[0]=(self.curDate[0]+self.curDate[1]/(numDays+1))
  380. self.curDate[2]=(self.curDate[2]+self.curDate[0]/(numMonths+1))
  381. self.curDate[1]=self.curDate[1]%(numDays+1)
  382. self.curDate[0]=self.curDate[0]%(numMonths+1)
  383. def increaseChronoByOne(self):
  384. self.curChrono[2]=self.curChrono[2]+1
  385. self.curChrono[1]=(self.curChrono[1]+self.curChrono[2]/100)
  386. self.curChrono[0]=(self.curChrono[0]+self.curChrono[1]/100)
  387. self.curChrono[2]=self.curChrono[2]%100
  388. self.curChrono[1]=self.curChrono[1]%100
  389. self.curChrono[0]=self.curChrono[0]%100
  390. def clearDisplay(self):
  391. if self.alarmTag != None:
  392. self.displayCanvas.delete(self.alarmTag)
  393. self.alarmTag = None
  394. if self.timeTag != None:
  395. self.displayCanvas.delete(self.timeTag)
  396. self.timeTag = None
  397. if self.chronoTag != None:
  398. self.displayCanvas.delete(self.chronoTag)
  399. self.chronoTag = None
  400. def clearDate(self):
  401. if self.dateTag != None:
  402. self.displayCanvas.delete(self.dateTag)
  403. self.dateTag = None
  404. def drawTime(self, toDraw=["hours","minutes","seconds"]):
  405. timeToDraw = self.__getTimeAsString()
  406. if "hours" not in toDraw:
  407. timeToDraw=" "+timeToDraw[2:]
  408. if "minutes" not in toDraw:
  409. timeToDraw=timeToDraw[0:3]+" "+timeToDraw[5:]
  410. if "seconds" not in toDraw:
  411. timeToDraw=timeToDraw[0:6]+" "
  412. if not self.battery:
  413. timeToDraw = "88:88:88"
  414. self.clearDisplay()
  415. self.timeTag =self.displayCanvas.create_text((RECT_X0+RECT_X1)/2,
  416. (RECT_Y0+RECT_Y1)/2+5,
  417. font = FONT_TIME,
  418. justify = "center",
  419. text = timeToDraw)
  420. def hideTime(self):
  421. if self.timeTag != None:
  422. self.displayCanvas.delete(self.timeTag)
  423. self.timeTag=None
  424. def drawChrono(self):
  425. chronoToDraw = self.__getChronoAsString()
  426. self.clearDisplay()
  427. if not self.battery:
  428. chronoToDraw = "88:88:88"
  429. self.chronoTag =self.displayCanvas.create_text((RECT_X0+RECT_X1)/2,
  430. (RECT_Y0+RECT_Y1)/2+5,
  431. font = FONT_TIME,
  432. justify = "center",
  433. text = chronoToDraw)
  434. def hideChrono(self):
  435. if self.chronoTag != None:
  436. self.displayCanvas.delete(self.chronoTag)
  437. self.chronoTag=None
  438. def resetChrono(self):
  439. self.curChrono=[0,0,0]
  440. def drawDate(self, toDraw=["years","months","days"]):
  441. dateToDraw = self.__getDateAsString()
  442. if "months" not in toDraw:
  443. dateToDraw=" "+dateToDraw[2:]
  444. if "days" not in toDraw:
  445. dateToDraw=dateToDraw[0:3]+" "+dateToDraw[5:]
  446. if "years" not in toDraw:
  447. dateToDraw=dateToDraw[0:6]+" "
  448. if not self.battery:
  449. dateToDraw = "88/88/88"
  450. self.clearDate()
  451. self.dateTag=self.displayCanvas.create_text(RECT_X1-33,
  452. RECT_Y0+7,
  453. font = FONT_DATE,
  454. justify = "center",
  455. text = dateToDraw)
  456. def drawAlarm(self, toDraw=["hours","minutes","seconds"]):
  457. alarmToDraw = self.__getAlarmAsString()
  458. if "hours" not in toDraw:
  459. alarmToDraw=" "+alarmToDraw[2:]
  460. if "minutes" not in toDraw:
  461. alarmToDraw=alarmToDraw[0:3]+" "+alarmToDraw[5:]
  462. if "seconds" not in toDraw:
  463. alarmToDraw=alarmToDraw[0:6]+" "
  464. if not self.battery:
  465. alarmToDraw = "88:88:88"
  466. self.clearDisplay()
  467. self.alarmTag =self.displayCanvas.create_text((RECT_X0+RECT_X1)/2,
  468. (RECT_Y0+RECT_Y1)/2+5,
  469. font = FONT_TIME,
  470. justify = "center",
  471. text = alarmToDraw)
  472. def hideAlarm(self):
  473. if self.alarmTag != None:
  474. self.displayCanvas.delete(self.alarmTag)
  475. self.alarmTag=None
  476. def setAlarm(self):
  477. if self.alarmNoteTag != None:
  478. self.displayCanvas.delete(self.alarmNoteTag)
  479. self.alarmNoteTag=None
  480. else:
  481. self.alarmNoteTag = self.displayCanvas.create_image(RECT_X0+5,RECT_Y0+3,image=self.noteImage,anchor="nw")
  482. def setIndiglo(self):
  483. self.displayCanvas.itemconfigure(self.display, fill='#96DCFA')
  484. def unsetIndiglo(self):
  485. self.displayCanvas.itemconfigure(self.display, fill="#DCDCDC")