DigitalWatchGUI.py 21 KB

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