DigitalWatchGUI.py 26 KB

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