· 9 years ago · Apr 06, 2017, 08:12 PM
1# NEA
2# Andrew Howell Candidate No. 0082
3# St. John Payne School Centre No. 16331
4
5runProgram = True
6
7##### Importing Python Modules needed for program #####
8
9# Defensive programming - catching exceptions in code where python modules are not present
10try:
11 import tkinter as tk
12 from tkinter import messagebox
13 from tkinter import *
14except:
15 print("Python's Tkinter module not installed, Program cannot run")
16 runProgram = False
17
18try:
19 import pickle
20except:
21 messagebox.showinfo("Error","Python's Pickle module not installed, Program cannot run")
22 runProgram = False
23
24try:
25 import datetime
26except:
27 messagebox.showinfo("Error","Python's Datetime module not installed, Program cannot run")
28 runProgram = False
29
30try:
31 import math
32except:
33 messagebox.showinfo("Error","Python's Datetime module not installed, Program cannot run")
34 runProgram = False
35
36try:
37 import random
38except:
39 messagebox.showinfo("Error","Python's Random module not installed, Program cannot run")
40 runProgram = False
41
42try:
43 import passlib
44 from passlib.hash import pbkdf2_sha256
45except:
46 messagebox.showinfo("Error","Python's Passlib module not installed, Program cannot run")
47 runProgram = False
48
49try:
50 import matplotlib
51 matplotlib.use("TkAgg")
52 from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
53 from matplotlib.figure import Figure
54 import matplotlib.animation as animation
55 from matplotlib import style
56except:
57 messagebox.showinfo("Error","Python's Matplotlib module not installed, Program cannot run")
58 runProgram = False
59
60try:
61 import numpy as np
62
63except:
64 messagebox.showinfo("Error","Python's NumPy module not installed, Program cannot run")
65 runProgram = False
66
67
68#######################################################
69
70
71
72
73
74##### Constant Declaration #####
75
76# Using passlib's hashing algorithm, password is securely encrypted
77HASHEDADMINPASSWORD = "$pbkdf2-sha256$29000$COH8X8sZQ6j1PkeoFUIIQQ$64MnDDlriVWgZGjjXEHP/HnlweMr/OkbAmAvgUGYDoA"
78
79ADMIN = ["Admin", HASHEDADMINPASSWORD] # Admin details
80
81LARGEFONT = ("Verdana", 22) # Font format for titles on each page
82MEDIUMFONT = ("Verdana", 16)
83LABELFONT = ("Verdana", 13)
84SMALLFONT = ("Verdana", 11)
85
86# Dimensions of the Tkinter window
87WIDTH = 800
88HEIGHT = 600
89
90FILENAME = "NEADatabase.dat" # Constant database name
91
92# Graph style
93style.use("ggplot")
94
95# Tkinter window RGB value
96BGCOLOUR = "#f0f0f0"
97
98################################
99
100
101
102
103
104##### File Handling #####
105
106# Using pickle because pickle allows a dictionary to be pickled directly to and from a file
107# Other file handling methods would need the dictionary to be broken down and then saved,
108# and when read, the dictionary would have to be manually reconstructed
109# Pickle makes file handling much simpler and faster and more efficient
110
111#Username Format: USERNAMES[Username] = [Password, Score, LastSession]
112USERNAMES = {} # Dictionary where student details will be stored whilst the program runs
113try:
114 try:
115 USERNAMES = pickle.load(open(FILENAME, "rb")) # Retrieves dictionary of user details stored in database
116 except:
117 pickle.dump({},open(FILENAME, "wb")) # If database not present in directory, create file
118except EOFError:
119 pass
120
121def saveUsers(): # Writes the current dictionary of user details to the database
122 pickle.dump(USERNAMES, open(FILENAME, "wb"))
123
124#########################
125
126
127
128
129
130##### Mathematical Operational Functions #####
131
132def Modulus(a): # Checks if the arguement is negative
133 if a < 0: # If so...
134 a = -a # Make it positive (negative of a negative is a positive)
135 return a # Returns positive number
136
137def mergeSort(alist): # A list is passed into the function
138 if len(alist) > 1: # Only performs splitting section of algorithm if the list is bigger than one element
139 mid = len(alist)//2 # Finds integer index position of middle element
140 lefthalf = alist[:mid] # Creates lists for first and second half of 'alist'
141 righthalf = alist[mid:] # Using [:mid] does not include the element in the 'mid' position so lists do not overlap
142
143 mergeSort(lefthalf) # Recursive calls
144 mergeSort(righthalf) # Continues to split lists until they containt only one element
145
146 leftHalfPosition = 0 # Starting position of pointer in left list
147 rightHalfPosition = 0 # Starting position of pointer in right list
148 newListPosition = 0 # Starting position in new merged list that will be sorted
149 while leftHalfPosition < len(lefthalf) and rightHalfPosition < len(righthalf): # If pointers are still in both lists
150 if lefthalf[leftHalfPosition] < righthalf[rightHalfPosition]: # Compares pointers in each list. If left list element is smaller...
151 alist[newListPosition] = lefthalf[leftHalfPosition] # Append that left list element to the new list
152 leftHalfPosition += 1 # Move left list pointer along one
153 else: # If right list element is smaller...
154 alist[originalListPosition] = righthalf[rightHalfPosition] # Append that right list element to the new list
155 rightHalfPosition += 1 # Move right list pointer along one
156 newListPosition += 1 # No matter which is smaller, new list will have one more element so move pointer along
157
158 while leftHalfPosition < len(lefthalf): # Right list elements exhausted, so only left list pointer still in list...
159 alist[newListPosition] = lefthalf[leftHalfPosition] # Append that left list element to the new list
160 leftHalfPosition += 1 # Move left list pointer along one
161 newListPosition += 1 # Move new list pointer along one
162
163 while rightHalfPosition < len(righthalf): # Left list elements exhausted, so only right list pointer still in list...
164 alist[newListPosition] = righthalf[rightHalfPosition] # Append that right list element to the new list
165 rightHalfPosition += 1 # Move right list pointer along one
166 newListPosition += 1 # Move new list pointer along one
167
168 return alist # Returns the newly merged list
169
170def getCurrentDate(): # Returns array of current day, month and year
171 day = datetime.date.today().day
172 month = datetime.date.today().month
173 year = datetime.date.today().year
174
175 date = [day, month, year]
176
177 return date
178
179def areaUnderCurve(xValues, yValues): # Two lists passed in as arguements
180
181 # Uses Trapezium Rule
182 # Area = 0.5h(y0 + yn + 2(y1 + y2 + ... + yn-1))
183 # As this is not a curve, but rather composed of straight lines, this algorithm will produce an accurate result
184 # Whereas ussually the trapezium rule only produces an estimate
185
186 if len(xValues) > 1 and len(yValues) > 1: # Algorithm needs lists longer than one element
187 n = len(yValues) - 1 # n is the number of 'gaps' between each vertical height line
188
189 h = (xValues[-1] - xValues[0])/n # h is the width of those gaps
190
191 ySubTotal = 0 # Variable that will store total of y values from y1 to yn-1
192 for each in yValues[1:-1]: # Refers to all elements in the yValues list that are between position 1 and position n-1
193 ySubTotal += each # Adds this y value to the total
194
195 area = 0.5*h*( (yValues[0] + yValues[-1]) + 2*ySubTotal ) # Trapezium rule equation
196
197 return area # Returns this newly found area
198 else: # If lists are not longer than 1, algorithm cannot run, so...
199 messagebox.showinfo("Error", "Not enough values given to find area under curve", icon="warning") # INform the user the algorithm needs more data
200 return "N/A"
201
202##############################################
203
204
205
206
207##### SUVAT Equation Solving Algorithm #####
208
209# This class is an example of polymorphism
210# Polymorphism is the method of using a single class to create many different objects performing different tasks
211# Each instantiation of the SUVAT object performs differently, calling different methods based on the arguements given
212
213class SUVAT():
214
215 def __init__(self,s,u,v,a,t,shortAnswer):
216
217 # Passes parameters from user as attributes
218 self.s = s
219 self.u = u
220 self.v = v
221 self.a = a
222 self.t = t
223 self.sA = shortAnswer # Boolean - determines whether object returns worded answer or just numeric answer
224
225 try:
226 def initiate(self): # Determines which method (and therefore which SUVAT eqaution) to call based on which parameters are 'x'
227 if self.s == "x":
228 return self.SUVAT1()
229 elif self.u == "x":
230 return self.SUVAT2()
231 elif self.v == "x":
232 return self.SUVAT3()
233 elif self.a == "x":
234 return self.SUVAT4()
235 elif self.t == "x":
236 return self.SUVAT5()
237 except:
238 messagebox.showinfo("Error","Invalid details inputted, try again", icon = "warning")
239
240 def SUVAT1(self): # v = u + at (s = 'X')
241
242 if self.u == "a":
243 self.u = float(self.v) - float(self.a)*float(self.t) # Converts string parameters given by user to a float value
244 if self.sA: # If the user wants a short answer -> using Checker not Solver
245 return [self.u,0] # Some calculations return two values so a 2 element array is returned
246 else:
247 return("Initial Velocity is {0:.2f} ms^-1".format(self.u))
248
249 elif self.v == "a":
250 self.v = float(self.u) + float(self.a)*float(self.t)
251 if self.sA:
252 return [self.v,0]
253 else:
254 return("Final Velocity is {0:.2f} ms^-1".format(self.v))
255
256 elif self.a == "a":
257 if float(self.t) == 0:
258 return "zeroDivision error"
259 else:
260 self.a = (float(self.v) - float(self.u))/float(self.t)
261 if self.sA:
262 return [self.a,0]
263 else:
264 return("Acceleration is {0:.2f} ms^-2".format(self.a))
265
266 elif self.t == "a":
267 if float(self.a) == 0:
268 return "zeroDivision error" # Calculation would include a division by zero which creates an error and so
269 # validation will inform the user that the question cannot include a division by zero
270 else:
271 self.t = (float(self.v) - float(self.u))/float(self.a)
272 if self.sA:
273 return [self.t,0]
274 else:
275 return("Time Taken is {0:.2f} s".format(self.t))
276
277 def SUVAT2(self): # s = vt - 0.5at^2 (u = 'X')
278
279 if self.s == "a":
280 self.s = float(self.v)*float(self.t) - 0.5*float(self.a)*float(self.t)**2
281 if self.sA:
282 return [self.s,0]
283 else:
284 return("Displacement is {0:.2f} m".format(self.s))
285
286 elif self.v == "a":
287 if float(self.t) == 0:
288 return "zeroDivision error"
289 else:
290 self.v = float(self.s)/float(self.t) + 0.5*float(self.a)*float(self.t)
291 if self.sA:
292 return [self.v,0]
293 else:
294 return("Final Velocity is {0:.2f} ms^-1".format(self.v))
295
296 elif self.a == "a":
297 if float(self.t) == 0:
298 return "zeroDivision error"
299 else:
300 self.a = (float(self.v)*float(self.t) - float(self.s))/(0.5*float(self.t)**2)
301 if self.sA:
302 return [self.a,0]
303 else:
304 return("Acceleration is {0:.2f} ms^-2".format(self.a))
305
306 elif self.t == "a":
307 if float(self.a) == 0:
308 return "zeroDivision error"
309 else:
310 x = float(self.v)**2 - 2*float(self.a)*float(self.s)
311 x = Modulus(x)
312 x = math.sqrt(x)
313 t1 = ((float(self.v) + (x))/float(self.a))
314 t2 = ((float(self.v) - (x))/float(self.a))
315 if self.sA:
316 return [t1,t2] # Two values of t
317 else:
318 return("Time Taken is {0:.2f} s and {1:.2f} s".format(t1, t2))
319
320 def SUVAT3(self): # s = ut + 0.5at^2 (v = 'X')
321
322 if self.s == "a":
323 self.s = float(self.u)*float(self.t) + 0.5*float(self.a)*float(self.t)**2
324 if self.sA:
325 return [self.s,0]
326 else:
327 return("Displacement is {0:.2f} m".format(self.s))
328
329 elif self.u == "a":
330 if float(self.t) == 0:
331 return "zeroDivision error"
332 else:
333 self.u = float(self.s)/float(self.t) - 0.5*float(self.a)*float(self.t)
334 if self.sA:
335 return [self.u,0]
336 else:
337 return("Initial Velocity is {0:.2f} ms^-1".format(self.u))
338
339 elif self.a == "a":
340 if float(self.t) == 0:
341 return "zeroDivision error"
342 else:
343 self.a = (float(self.s) - float(self.u)*float(self.t))/(0.5*float(self.t)**2)
344 if self.sA:
345 return [self.a,0]
346 else:
347 return("Acceleration is {0:.2f} ms^-2".format(self.a))
348
349 elif self.t == "a":
350 if float(self.a) == 0:
351 return "zeroDivision error"
352 else: # Using quadratic equation - returns two values of t
353 x = float(self.u)**2 + 2*float(self.a)*float(self.s)
354 x = Modulus(x)
355 x = math.sqrt(x)
356 t1 = ((-float(self.u) + (x))/float(self.a))
357 t2 = ((-float(self.u) - (x))/float(self.a))
358 if self.sA:
359 return [t1,t2]
360 else:
361 return("Time Taken is {0:.2f} s and {1:.2f} s".format(t1, t2))
362
363 def SUVAT4(self): # s = 0.5(u + v)t (a = 'X')
364
365 if self.s == "a":
366 self.s = 0.5*(float(self.u) + float(self.v))*float(self.t)
367 if self.sA:
368 return [self.s,0]
369 else:
370 return("Displacement is {0:.2f} m".format(self.s))
371
372 elif self.u == "a":
373 if float(self.t) == 0:
374 return "zeroDivision error"
375 else:
376 self.u = (2*float(self.s))/float(self.t) - float(self.v)
377 if self.sA:
378 return [self.u,0]
379 else:
380 return("Initial Velocity is {0:.2f} ms^-1".format(float(self.u)))
381
382 elif self.v == "a":
383 if float(self.t) == 0:
384 return "zeroDivision error"
385 else:
386 self.v = (2*float(self.s))/float(self.t) - float(self.u)
387 if self.sA:
388 return [self.v,0]
389 else:
390 return("Final Velocity is {0:.2f} ms^-1".format(self.v))
391
392 elif self.t == "a":
393 if (float(self.u) + float(self.v)) == 0:
394 return "zeroDivision error"
395 else:
396 self.t = (2*float(self.s))/(float(self.u) + float(self.v))
397 if self.sA:
398 return [self.t,0]
399 else:
400 return("Time Taken is {0:.2f} s".format(self.t))
401
402 def SUVAT5(self): # v^2 = u^2 + 2as (t = 'X')
403
404 if self.s == "a":
405 if float(self.a) == 0:
406 return "zeroDivision error"
407 else:
408 self.s = (float(self.v)**2 - float(self.u)**2)/2*float(self.a)
409 if self.sA:
410 return [self.s,0]
411 else:
412 return("Displacement is {0:.2f} m".format(self.s))
413
414 elif self.u == "a":
415 uSquared = float(self.v)**2 - 2*float(self.a)*float(self.s)
416 self.u = math.sqrt(Modulus(uSquared)) # If self.u is negative,
417 # square rooting it will cause
418 # an error, so we square root the modulus
419 if self.sA:
420 return [self.u,-self.u]
421 else:
422 return("Initial Velocity is {0:.2f} ms^-1 and {1:.2f} ms^-1".format(self.u,-self.u))
423
424 elif self.v == "a":
425 vSquared = float(self.u)**2 + 2*float(self.a)*float(self.s)
426 self.v = math.sqrt(Modulus(vSquared))
427 if self.sA:
428 return [self.v,-self.v]
429 else:
430 return("Final Velocity is {0:.2f} ms^-1 and {1:.2f} ms^-1".format(self.v,-self.v))
431
432 elif self.a == "a":
433 if float(self.s) == 0:
434 return "zeroDivision error"
435 else:
436 self.a = (float(self.v)**2 - float(self.u)**2)/2*float(self.s)
437 if self.sA:
438 return [self.a,0]
439 else:
440 return("Acceleration is {0:.2f} ms^-2".format(self.a))
441
442############################################
443
444
445
446
447
448##### Matplotlib Animation Function #####
449
450# Creates the backend of the graph
451f = Figure(figsize=(8,6), dpi=80)
452a = f.add_subplot(111)
453a.axhline(linewidth=0.5, color="k")
454a.axvline(linewidth=0.5, color="k")
455a.set_title("Distance-Time Graph")
456a.set_ylabel('distance /m')
457a.set_xlabel('time /s')
458
459# An example of composition
460# This class is used as a repository for all the information contained within the graph
461# The GraphController class within the interactiveGraphsPage class utilises this class
462# It holds the current state of the graph in its attributes and its methods alter those attributes
463
464class animationObject():
465 def __init__(self):
466 self.play = False
467 self.title = "Distance-Time Graph"
468 self.ylabel = "distance /m"
469 self.xlabel = "time /s"
470 self.xCounter = 1 # X coordinate, simply increments by 1 every time that the ainmate function is called
471 self.xList = [0] # X coordinates of points on the graph
472 self.yList = [0] # Y coordinates of points on the graph
473 def changeState(self): # Changes state, from paused to running, or vice versa
474 if self.play == False:
475 self.play = True
476 elif self.play == True:
477 self.play = False
478 def gradientUnits(self): # Calculates units from the current graphs y axis label
479 yUnit = self.ylabel.split("/")[1] # Splits the ylabel into an array, the letters after '/' are stored in yUnit
480 if yUnit == "m": # Gradient will be the y unit divided by time
481 return "ms^-1" # So if the y unit is metres, the gradient will be metres per second or ms^-1
482 if yUnit == "ms^-1":
483 return "ms^-2"
484 if yUnit == "ms^-2":
485 return "ms^-3"
486 def areaUnits(self): # Calculates units from the current graphs y axis label
487 yUnit = self.ylabel.split("/")[1] # Splits the ylabel into an array, the letters after '/' are stored in yUnit
488 if yUnit == "m": # Area will be the y unit mulitplied by time
489 return "ms" # So if the y unit is metres, the area will be metre seconds or ms
490 if yUnit == "ms^-1":
491 return "m"
492 if yUnit == "ms^-2":
493 return "ms^-1"
494
495animationState = animationObject()
496
497# Animate function is called every half second, it updates the backend of the graph
498def animate(i):
499 a.axhline(linewidth=0.5, color="k")
500 a.axvline(linewidth=0.5, color="k")
501 a.set_title(animationState.title)
502 a.set_ylabel(animationState.ylabel)
503 a.set_xlabel(animationState.xlabel)
504 if animationState.play == True: # Only if the user has told the program to run the graph, will it update / animate
505 xList = animationState.xList
506 yList = animationState.yList
507 # Distance and Speed graphs are scalar and cannot be negative
508 # This if statement stops the graph from updating if it is going to become zero and it is one of these scalar graphs
509 if yList[int((animationState.xCounter)-1)] + window.frames[interactiveGraphsPage].graphController.gradientScale.get() < 0 and "distance" in animationState.ylabel or "speed" in animationState.ylabel:
510 animationState.changeState()
511 window.frames[interactiveGraphsPage].graphController.currentState.configure(text="Graph is: Paused")
512 messagebox.showinfo("Warning","Graphs of this type cannot be negative\n\nDistance and Speed are Scalar Quantities\n\nScalar Quantities cannot be negative", icon="warning")
513 window.frames[interactiveGraphsPage].graphController.gradientScale.set(0)
514 else: # If the graph can continue, it updates the lists of coordinates and creates the new graph
515 yList.append(yList[int((animationState.xCounter)-1)] + window.frames[interactiveGraphsPage].graphController.gradientScale.get())
516 xList.append(animationState.xCounter)
517
518 a.clear()
519 a.plot(xList, yList)
520 a.axhline(linewidth=0.5, color="k")
521 a.axvline(linewidth=0.5, color="k")
522 a.set_title(animationState.title)
523 a.set_ylabel(animationState.ylabel)
524 a.set_xlabel(animationState.xlabel)
525 animationState.xCounter = animationState.xCounter + 1
526
527#########################################
528
529
530
531
532
533##### GUI Classes #####
534
535# Another example of composition
536# GUI class uses Tkinter's Frames as pages in the Tkinter window
537# Each of these Frames are objects instantiated from the child classes
538class GUI(tk.Tk):
539
540 def __init__(self, *args, **kwargs): # Allows a variable amount of keyworded and non-keyworded arguements to be passed into the object
541
542 self.activeUser = "" # Stores the Username of a current user
543
544 tk.Tk.__init__(self, *args, **kwargs)
545
546 tk.Tk.iconbitmap(self, default="NEALogo.ico") # Displays telescope icon to be displayed in left-hand corner of the GUI window
547 tk.Tk.wm_title(self, "Physics Learning Aid") # Displays on the header of th GUI window
548
549 container = tk.Frame(self) # Container holds all elements of the frames and the toolbar
550 container.pack(side="top", fill="both", expand = True)
551 container.grid_rowconfigure(0, weight=1) # Weight attribute added to allow the rows and colunmns to display properly
552 container.grid_columnconfigure(0, weight=1)
553
554 menubar = tk.Menu(container) # Creates the toolbar where students can easily access different pages
555 self.filemenu = tk.Menu(menubar, tearoff=0)
556 self.filemenu.add_command(label="Home", state="disable", command = lambda: self.show_frame(homePage)) # Pages disabled until a user logs in
557 self.filemenu.add_command(label="1: Forces", state="disable", command = lambda: self.show_frame(forcesPage))
558 self.filemenu.add_command(label="2: Time Graphs", state="disable", command = lambda: self.show_frame(timeGraphsPage))
559 self.filemenu.add_command(label="3. SUVAT Revision", state="disable", command = lambda: self.show_frame(SUVATRevisionPage))
560 self.filemenu.add_command(label="Account", command = lambda: self.show_frame(accountPage))
561
562 helpmenu = tk.Menu(self.filemenu, tearoff=0) # Dropdown menu
563 helpmenu.add_command(label="General Help", command=self.helpme)
564 helpmenu.add_command(label="Application Help", command=self.helpme)
565 helpmenu.add_command(label="SUVAT Solver Help", command=self.helpme)
566 self.filemenu.add_cascade(label="Help", menu=helpmenu)
567
568 tk.Tk.config(self, menu=self.filemenu)
569
570 self.frames = {}
571
572 # Creates each individual page as an object is instanciated from the page classes
573 for page in (startPage, accountPage, changePasswordPage, loginPage, signupPage, adminLoginPage, adminPage, homePage, forcesPage, ScalarsVectorsPage, AdditionVectorsCalculationPage,
574 AdditionVectorsScaleDrawingPage, ResolutionOfVectorsPage, InclinedPlaneVectorsPage, VectorsInEquilibriumPage, timeGraphsPage, GradientsPage, AreaUnderCurvePage, BouncingBallPage,
575 interactiveGraphsPage, SUVATRevisionPage, SUVATCheckerPage, SUVATSolverPage):
576
577 frame = page(container, self)
578
579 self.frames[page] = frame
580
581 frame.grid(row=0, column=0, sticky="nsew")
582
583 self.centreScreen(WIDTH, HEIGHT)
584
585 def helpme(self):
586 helpList = ["Please seek help from your teacher", "Ask your teacher for help, they're right behind you!", "Help Bot Open Hours -> 11:59PM SUN - 00:00AM MON\nPlease come back then", "Tough, help yourself, I'M BUSY",
587 "I'm in the Bahamas on holiday right now\n\nLEAVE\n\nME\n\nALONE"]
588 helpChoice = random.randrange(len(helpList))
589 helpComment = helpList[helpChoice-1]
590 messagebox.showinfo("Help Bot","Hi, you have requested help\n\n{}".format(helpComment), icon="info")
591
592 def enableWidgets(self):
593 self.filemenu.entryconfig("Home", state="normal")
594 self.filemenu.entryconfig("1: Forces", state="normal")
595 self.filemenu.entryconfig("2: Time Graphs", state="normal")
596 self.filemenu.entryconfig("3. SUVAT Revision", state="normal")
597
598 def disableWidgets(self):
599 self.filemenu.entryconfig("Home", state="disable")
600 self.filemenu.entryconfig("1: Forces", state="disable")
601 self.filemenu.entryconfig("2: Time Graphs", state="disable")
602 self.filemenu.entryconfig("3. SUVAT Revision", state="disable")
603
604 def addScore(self, score):
605 if window.activeUser != "Admin" and window.activeUser != "":
606 USERNAMES[self.activeUser][1] += score
607
608 def centreScreen(self, w, h): # Uses screen dimensions to centre the GUI window
609
610 ws = self.winfo_screenwidth()
611 hs = self.winfo_screenheight()
612
613 # Finds the top left coordinate that the GUI will be in order to be centered
614 x = (ws / 2) - (w / 2)
615 y = (hs / 2) - (w / 2)
616
617 # Align window with the top left coordinate
618 self.geometry("%dx%d+%d+%d" % (w, h, x, y)) # takes parameters as 'wxh+x+y'
619 self.resizable(width=False, height=False)
620
621 # Sets page that program will start with
622 self.show_frame(startPage)
623
624 def logout(self):
625 if self.activeUser != "" and self.activeUser != ADMIN[0]:
626 date = getCurrentDate()
627 USERNAMES[self.activeUser][2] = date
628 self.activeUser = ""
629 self.disableWidgets()
630 messagebox.showinfo("Logged out", "You have been logged out successfully")
631 saveUsers()
632 self.show_frame(startPage)
633
634 def show_frame(self, cont):
635
636 frame = self.frames[cont]
637
638 if cont.__name__ != "interactiveGraphsPage": # Checks if the new page to be displayed is not called 'interactiveGraphsPage'
639 try:
640 self.frames[interactiveGraphsPage].graphController.destroy() # If it is, try to destroy the graph controller window
641 except: # The except statement will run if the graph controller window is not open
642 pass # In which case, ignore and continue
643
644 frame.tkraise() # When called it raises the given page to the top of the GUI
645
646### Start of Page Classes ###
647
648class startPage(tk.Frame):
649
650 def __init__(self, parent, controller):
651
652 tk.Frame.__init__(self, parent)
653 self.title = tk.Label(self, text="Start Page", font=LARGEFONT)
654 self.title.pack(side="top", fill="x", pady=30)
655
656 self.welcomeMsg = tk.Label(self, text = "Welcome to the Physics Learning Aid!")
657 self.label1 = tk.Label(self, text = "Are you a")
658 self.student = tk.Button(self, text = "Student", command=lambda: controller.show_frame(accountPage),height=2,width=20)
659 self.label2 = tk.Label(self, text = "or ")
660 self.teacher = tk.Button(self, text = "Teacher", command=lambda: controller.show_frame(adminLoginPage),height=2,width=20)
661
662 self.welcomeMsg.pack(side="top", fill="x", padx=5, pady=20)
663 self.label1.pack(side="top", fill="x", pady=5)
664 self.student.pack(side="top", pady=5)
665 self.label2.pack(side="top", fill="x", pady=5)
666 self.teacher.pack(side="top", pady=5)
667
668class accountPage(tk.Frame):
669
670 def __init__(self, parent, controller):
671
672 tk.Frame.__init__(self, parent)
673 self.title = tk.Label(self, text="Account Page", font=LARGEFONT)
674 self.title.pack(side="top", fill="x", pady=30)
675
676 self.login = tk.Button(self, text = "Existing User - Log in", command=lambda: controller.show_frame(loginPage),height=2,width=20)
677 self.signUp = tk.Button(self, text = "New User - Sign Up", command=lambda: controller.show_frame(signupPage),height=2,width=20)
678 self.logoutButton = tk.Button(self, text = "Log out", command=lambda: window.logout())
679 self.selectButton = tk.Button(self,text = "Score", command=lambda: showScore())
680 self.changePasswordButton = tk.Button(self, text = "Change Password", command=lambda: changePassword())
681
682 self.login.pack(side="top", fill="x", padx=300, pady=20)
683 self.signUp.pack(side="top", fill="x", padx=300, pady=5)
684 self.selectButton.pack(side="top", pady=20)
685 self.logoutButton.pack(side="bottom", pady=10)
686 self.changePasswordButton.pack(side="bottom")
687
688 # Displays the user's details, their username, current score and the date they last were on the program
689 def showScore():
690 if window.activeUser != "" and window.activeUser != "Admin":
691 score = USERNAMES[window.activeUser][1]
692 messagebox.showinfo("Your Details","Username : {0}\n\nScore : {1}".format(window.activeUser,score), icon="info")
693 else:
694 messagebox.showinfo("Error","Cannot display score\n\nYou are not logged in as a student", icon="warning")
695
696 def changePassword():
697 if window.activeUser != "" and window.activeUser != "Admin": # Don't switch to changePasswordPage if noone is logged off, or the admin is logged in
698 controller.show_frame(changePasswordPage)
699 else:
700 messagebox.showinfo("Error","Please login before trying to change your password", icon="warning")
701 self.login.focus()
702
703class changePasswordPage(tk.Frame):
704
705 def __init__(self, parent, controller):
706
707 tk.Frame.__init__(self, parent)
708 self.title = tk.Label(self, text="Change Password", font=LARGEFONT)
709 self.title.pack(padx=10, pady=10)
710
711 self.originalPasswordText = tk.Label(self, text = "Original Password:")
712 self.originalPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
713
714 self.newPasswordText = tk.Label(self, text = "New Password:")
715 self.newPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
716
717 self.confirmPasswordText = tk.Label(self, text = "Confirm Password:")
718 self.confirmPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
719
720 self.changePasswordButton = tk.Button(self,text = "Change Password", command=lambda: changePassword())
721
722 self.originalPasswordText.pack(side="top", fill="x", padx=20, pady=5)
723 self.originalPasswordInput.pack(side="top")
724 self.originalPasswordInput.focus()
725
726 self.newPasswordText.pack(side="top", fill="x", padx=20, pady=5)
727 self.newPasswordInput.pack(side="top")
728
729 self.confirmPasswordText.pack(side="top", fill="x", padx=20, pady=5)
730 self.confirmPasswordInput.pack(side="top")
731
732 self.changePasswordButton.pack(side="top", fill="y", padx=20, pady=10)
733
734 def changePassword():
735
736 if self.originalPasswordInput.get() == USERNAMES[window.activeUser][0]:
737 if self.newPasswordInput.get() == self.confirmPasswordInput.get():
738 USERNAMES[window.activeUser][0] = self.newPasswordInput.get()
739 messagebox.showinfo("Password Changed","Your password has been changed successfully!", icon="info")
740 controller.show_frame(accountPage)
741 else:
742 messagebox.showinfo("Error","Your new and confirmation password do not match\nPlease try again", icon="warning")
743 self.newPasswordInput.delete(0,"end")
744 self.confirmPasswordInput.delete(0,"end")
745 self.newPasswordInput.focus()
746 else:
747 messagebox.showinfo("Error","Incorrect original password\nPlease try again", icon="warning")
748 self.originalPasswordInput.delete(0,"end")
749 self.newPasswordInput.delete(0,"end")
750 self.confirmPasswordInput.delete(0,"end")
751 self.newPasswordInput.focus()
752
753class loginPage(tk.Frame):
754
755 def __init__(self, parent, controller):
756
757 tk.Frame.__init__(self, parent)
758 self.title = tk.Label(self, text="Login", font=LARGEFONT)
759 self.title.pack(side="top", fill="x", pady=30)
760
761 # User details entry boxes
762 self.usernameText = tk.Label(self, text = "Username:")
763 self.usernameInput = tk.Entry(self, justify="center", width=30)
764 self.passwordText = tk.Label(self, text = "Password:")
765 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
766
767 self.loginButton = tk.Button(self,text = "Login", command=lambda: login())
768
769 self.usernameText.pack(side="top", fill="x", padx=20, pady=5)
770 self.usernameInput.pack(side="top", pady=5)
771 self.usernameInput.focus()
772 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
773 self.passwordInput.pack(side="top", pady=5)
774 self.loginButton.pack(side="top", pady="10")
775
776 def login(): # Validation ensuring username exists, and that the password given matches the saved password
777 if self.usernameInput.get() in USERNAMES: # Checks if the username exists in the database
778 if self.passwordInput.get() == USERNAMES[self.usernameInput.get()][0]: # Checks the password matches that of the stored password
779 window.activeUser = self.usernameInput.get()
780 messagebox.showinfo("Login Successful","Welcome back {0}".format(self.usernameInput.get()), icon = "info")
781 self.usernameInput.delete(0,"end") # Deletes value in entry box as it will not be removed automatically when a new frame is raised
782 self.passwordInput.delete(0,"end")
783 controller.show_frame(homePage)
784 window.enableWidgets()
785 else:
786 self.passwordInput.delete(0,"end")
787 messagebox.showinfo("Login Failed", "Incorrect Password", icon = "warning")
788 self.passwordInput.focus()
789 else:
790 self.usernameInput.delete(0,"end")
791 self.passwordInput.delete(0,"end")
792 messagebox.showinfo("Login Failed", "Username not found, please try again if you are an existing user\nIf not, register by clicking on the account tab at the top", icon = "warning")
793 self.usernameInput.focus()
794
795class signupPage(tk.Frame):
796
797 def __init__(self, parent, controller):
798
799 tk.Frame.__init__(self, parent)
800 title = tk.Label(self, text="Sign Up", font=LARGEFONT)
801 title.pack(padx=10, pady=10)
802
803 self.firstNameText = tk.Label(self, text = "First Name:")
804 self.firstNameInput = tk.Entry(self, justify="center", width=30)
805
806 self.secondNameText = tk.Label(self, text = "Second Name:")
807 self.secondNameInput = tk.Entry(self, justify="center", width=30)
808
809 self.yearOfEntryText = tk.Label(self, text = "Year of Entry to SJP (YYYY):")
810 self.yearOfEntryInput = tk.Entry(self, justify="center", width=30)
811
812 self.passwordText = tk.Label(self, text = "New Password:")
813 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
814
815 self.passwordText2 = tk.Label(self, text = "Confirm Password:")
816 self.passwordInput2 = tk.Entry(self, show="*", justify="center", width=30)
817
818 self.signupButton = tk.Button(self,text = "Register", command=lambda: signup())
819
820 # Pack the elements onto the GUI window
821 self.firstNameText.pack(side="top", fill="x", padx=20, pady=5)
822 self.firstNameInput.pack(side="top")
823 self.firstNameInput.focus()
824
825 self.secondNameText.pack(side="top", fill="x", padx=20, pady=5)
826 self.secondNameInput.pack(side="top")
827
828 self.yearOfEntryText.pack(side="top", fill="x", padx=20, pady=5)
829 self.yearOfEntryInput.pack(side="top")
830
831 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
832 self.passwordInput.pack(side="top")
833
834 self.passwordText2.pack(side="top", fill="x", padx=20, pady=5)
835 self.passwordInput2.pack(side="top")
836
837 self.signupButton.pack(side="top", fill="y", padx=20, pady=10)
838
839 def signup():
840
841 # Create local variables that are easier to manipulate inside the function
842 firstName = self.firstNameInput.get()
843 secondName = self.secondNameInput.get()
844 yearOfEntry = self.yearOfEntryInput.get()
845 password = self.passwordInput.get()
846
847 if firstName == "" or secondName == "" or yearOfEntry == "" or password == "":
848 messagebox.showinfo("Error!","Please do not leave any fields blank", icon = "warning")
849 else:
850 if yearOfEntry.isdigit() == False or len(yearOfEntry) != 4:
851 messagebox.showinfo("Invalid Year of Entry", "Please enter a valid 4 digit year", icon = "warning")
852 self.yearOfEntryInput.delete(0,"end")
853 self.yearOfEntryInput.focus()
854
855 else:
856 if self.passwordInput.get() != self.passwordInput2.get():
857 messagebox.showinfo("Passwords do not match", "The passwords you entered did not match\n\nPlease ensure they match", icon = "warning")
858 self.passwordInput.delete(0,"end")
859 self.passwordInput2.delete(0,"end")
860 self.passwordInput.focus()
861
862 else:
863 newUsername = yearOfEntry[2:] + secondName.lower() + firstName[0].lower() # Creates a username, compiled from the student's year of entry, their surname and the first initial of their forename
864 if newUsername in USERNAMES:
865 messagebox.showinfo("Error!","That user already exists\n\nIf someone in your year shares your name, add a space on the end of your surname", icon = "warning")
866 self.firstNameInput.delete(0,"end")
867 self.secondNameInput.delete(0,"end")
868 self.yearOfEntryInput.delete(0,"end")
869 self.passwordInput.delete(0,"end")
870 self.passwordInput2.delete(0,"end")
871 self.firstNameInput.focus() # Resets the form and focuses the keyboard onto the first entry field
872 else:
873 revisionScore = 0 # Each students starts with a score of 0, this will be added to and subtracted from when they perform tasks on the program
874 lastSession = [] # This will hold the date which the student last logged out on, it is overwritten when the user logs out of the program
875 USERNAMES[newUsername] = [password, revisionScore, lastSession] # Creates entry in the USERNAMES dictionary holding all the usernames, will later be pickled to the database when logging out
876
877 window.activeUser = newUsername # Assigns the active user as the newly registered user
878
879 messagebox.showinfo("Registered","Welcome {0}".format(newUsername), icon = "info")
880
881 self.firstNameInput.delete(0,"end")
882 self.secondNameInput.delete(0,"end")
883 self.yearOfEntryInput.delete(0,"end")
884 self.passwordInput.delete(0,"end")
885 self.passwordInput2.delete(0,"end")
886 window.enableWidgets()
887 controller.show_frame(homePage)
888
889class adminLoginPage(tk.Frame):
890
891 def __init__(self, parent, controller):
892
893 tk.Frame.__init__(self, parent)
894 self.title = tk.Label(self, text="Admin Login Page", font=LARGEFONT)
895 self.title.pack(side="top", fill="x", pady=30)
896
897 self.usernameText = tk.Label(self, text = "Admin Username:")
898 self.usernameInput = tk.Entry(self, justify="center", width=30)
899 self.passwordText = tk.Label(self, text = "Admin Password:")
900 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
901
902 self.loginButton = tk.Button(self,text = "Login", command=lambda: adminLogin())
903
904 self.usernameText.pack(side="top", fill="x", padx=20, pady=5)
905 self.usernameInput.pack(side="top", pady=5)
906 self.usernameInput.focus()
907 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
908 self.passwordInput.pack(side="top", pady=5)
909 self.loginButton.pack(side="top", pady="10")
910
911 def adminLogin():
912 if self.usernameInput.get() == ADMIN[0]:
913 if pbkdf2_sha256.verify(self.passwordInput.get(), ADMIN[1]):
914 window.activeUser = self.usernameInput.get()
915 messagebox.showinfo("Login Successful","Continue to Admin Page".format(self.usernameInput.get()), icon = "info")
916 self.usernameInput.delete(0,"end")
917 self.passwordInput.delete(0,"end")
918 controller.show_frame(adminPage)
919 window.enableWidgets()
920 else:
921 self.usernameInput.delete(0,"end")
922 self.passwordInput.delete(0,"end")
923 messagebox.showinfo("Login Failed", "Incorrect details", icon = "warning")
924 self.usernameInput.focus()
925 else:
926 self.usernameInput.delete(0,"end")
927 self.passwordInput.delete(0,"end")
928 messagebox.showinfo("Login Failed", "Incorrect details", icon = "warning")
929 self.usernameInput.focus()
930
931class adminPage(tk.Frame):
932
933 def __init__(self, parent, controller):
934
935 tk.Frame.__init__(self, parent)
936 self.title = tk.Label(self, text="Admin Page", font=LARGEFONT)
937 self.title.pack(padx=10, pady=10)
938
939 username = StringVar()
940 username.set("Usernames")
941
942 self.usernameText = tk.Label(self, text = "Select students to review details:")
943 self.usernamesList = list(USERNAMES) # Creates an array, holding all the key values from the dictionary USERNAMES
944 if self.usernamesList == []:
945 self.usernamesList.append("No active users")
946 self.dropdown = OptionMenu(self, username, *self.usernamesList)
947
948 self.updateButton = tk.Button(self,text = "Update and Sort (Alphabetically)", command=lambda: updateUsernames())
949 self.selectButton = tk.Button(self,text = "Search", command=lambda: searchUsername())
950
951 self.usernameText.pack(side="top", padx=20, pady=5)
952 self.dropdown.pack(side="top", padx=20, pady=5)
953 self.updateButton.pack(side="top", pady=10)
954 self.selectButton.pack(side="top")
955
956 # Displays the user's details, their username, current score and the date they last were on the program
957 def searchUsername():
958 if username.get() in USERNAMES:
959 score = USERNAMES[username.get()][1]
960 lastSession = USERNAMES[username.get()][2]
961 messagebox.showinfo("User Details","Username : {0}\n\nScore : {1}\n\nLast Session : {2}/{3}/{4}".format(username.get(),score,lastSession[0],lastSession[1],lastSession[2]))
962 username.set("Usernames")
963
964 # Updates the usernames list, sorting them into alphabetical order of their username, allowing the teacher to easily search for a student
965 def updateUsernames():
966 username.set("Usernames")
967 self.dropdown["menu"].delete(0,"end")
968
969 self.usernamesList = list(USERNAMES) # Takes into account any new users
970 self.usernamesList = mergeSort(self.usernamesList) # Calls global function to merge sort the list of
971 for each in self.usernamesList:
972 self.dropdown["menu"].add_command(label=each, command=tk._setit(username, each))
973
974class homePage(tk.Frame):
975
976 def __init__(self, parent, controller):
977
978 tk.Frame.__init__(self, parent)
979 self.title = tk.Label(self, text="Learning Options Page", font=LARGEFONT)
980 self.title.pack(padx=10, pady=10)
981
982 self.welcomeMsg = tk.Label(self, text = "Welcome to the Physics Learning Aid!")
983 self.forcesButton = tk.Button(self, text = "1. Forces", command=lambda: controller.show_frame(forcesPage),height=2,width=20)
984 self.label1 = tk.Label(self, text = "or ")
985 self.timeGraphsButton = tk.Button(self, text = "2. Time Graphs", command=lambda: controller.show_frame(timeGraphsPage),height=2,width=20)
986 self.label2 = tk.Label(self, text = "or ")
987 self.SUVATRevisionButton = tk.Button(self, text = "3. SUVAT Revision", command=lambda: controller.show_frame(SUVATRevisionPage),height=2,width=20)
988
989 self.welcomeMsg.pack(side="top", fill="x", padx=5, pady=20)
990 self.forcesButton.pack(side="top", pady=5)
991 self.label1.pack(side="top", fill="x", pady=5)
992 self.timeGraphsButton.pack(side="top", pady=5)
993 self.label2.pack(side="top", fill="x", pady=5)
994 self.SUVATRevisionButton.pack(side="top", pady=5)
995
996# Forces Pages #
997
998class forcesPage(tk.Frame):
999
1000 def __init__(self, parent, controller):
1001
1002 tk.Frame.__init__(self, parent)
1003 self.title = tk.Label(self, text="Forces Page", font=LARGEFONT)
1004 self.title.pack(padx=10, pady=10)
1005
1006 self.label = tk.Label(self, text = "Forces Topics:", font = MEDIUMFONT)
1007 self.ScalarsVectorsButton = tk.Button(self, text = "Scalars and Vectors", command=lambda: controller.show_frame(ScalarsVectorsPage))
1008 self.AdditionVectorsCalculationButton = tk.Button(self, text = "Addition of Vectors - Calculation", command=lambda: controller.show_frame(AdditionVectorsCalculationPage))
1009 self.AdditionVectorsScaleDrawingButton = tk.Button(self, text = "Addition of Vectors - Scale Drawing", command=lambda: controller.show_frame(AdditionVectorsScaleDrawingPage))
1010 self.ResolutionOfVectorsButton = tk.Button(self, text = "Resolution of Vectors", command=lambda: controller.show_frame(ResolutionOfVectorsPage))
1011 self.VectorsInEquilibriumButton = tk.Button(self, text = "Vectors in Equilibrium", command=lambda: controller.show_frame(VectorsInEquilibriumPage))
1012 self.InclinedPlaneVectorsButton = tk.Button(self, text = "Inclined Plane Vectors", command=lambda: controller.show_frame(InclinedPlaneVectorsPage))
1013
1014 self.label.pack(side="top", fill="x", pady=10)
1015 self.ScalarsVectorsButton.pack(side="top", pady=15, ipadx=10)
1016 self.AdditionVectorsCalculationButton.pack(side="top", pady=15, ipadx=10)
1017 self.AdditionVectorsScaleDrawingButton.pack(side="top", pady=15, ipadx=10)
1018 self.ResolutionOfVectorsButton.pack(side="top", pady=15, ipadx=10)
1019 self.VectorsInEquilibriumButton.pack(side="top", pady=15, ipadx=10)
1020 self.InclinedPlaneVectorsButton.pack(side="top", pady=15, ipadx=10)
1021
1022class ScalarsVectorsPage(tk.Frame):
1023
1024 def __init__(self, parent, controller):
1025
1026 tk.Frame.__init__(self, parent)
1027 self.title = tk.Label(self, text="Scalars and Vectors Page", font=LARGEFONT)
1028 self.title.pack(padx=10, pady=10)
1029
1030 self.canvas = Canvas(self, width=800, height=500, bd=2)
1031 self.canvas.pack()
1032
1033 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1034 self.returnButton.pack(ipadx=10)
1035
1036 self.canvas.create_line(400, 0, 400, 350)
1037 self.canvas.create_line(400, 400, 400, 500)
1038 self.canvas.create_line(20, 375, 325, 375)
1039 self.canvas.create_line(475, 375, 780, 375)
1040
1041 self.canvas.create_text(200, 15, text = "Scalars", font = MEDIUMFONT)
1042 self.canvas.create_line(100, 175, 300, 75, arrow=BOTH, arrowshape="2 1 10")
1043 self.canvas.create_text(160, 100, text = "2 metres", font = LABELFONT)
1044 self.canvas.create_text(200, 250, text = "Scalars have size / length / magnitude", font = LABELFONT)
1045 self.canvas.create_text(200, 300, text = "They have no direction", font = MEDIUMFONT)
1046
1047 self.canvas.create_text(600, 15, text = "Vectors", font = MEDIUMFONT)
1048 self.canvas.create_text(500, 60, text = "North", font = LABELFONT)
1049 self.canvas.create_line(500, 175, 500, 75, arrow=LAST) # North arrow
1050 self.canvas.create_line(500, 175, 700, 75, arrow=LAST) # Scalar arrow
1051 self.canvas.create_arc(475, 150, 525, 200, start = 90.0, extent = -63.44)
1052 self.canvas.create_text(660, 135, text = "2 metres", font = LABELFONT)
1053 self.canvas.create_text(520, 140, text = "Θ", font = LABELFONT)
1054 self.canvas.create_text(600, 250, text = "Vectors have size / length / magnitude", font = LABELFONT)
1055 self.canvas.create_text(600, 300, text = "They also have a direction / bearing", font = LABELFONT)
1056
1057 self.canvas.create_text(400, 375, text = "Examples", font = MEDIUMFONT)
1058
1059 self.canvas.create_text(120, 435, text = "Speed", font = MEDIUMFONT)
1060 self.canvas.create_text(200, 470, text = "Mass", font = MEDIUMFONT)
1061 self.canvas.create_text(280, 435, text = "Distance", font = MEDIUMFONT)
1062
1063 self.canvas.create_text(465, 435, text = "Velocity", font = MEDIUMFONT)
1064 self.canvas.create_text(515, 470, text = "Force", font = MEDIUMFONT)
1065 self.canvas.create_text(565, 435, text = "Weight", font = MEDIUMFONT)
1066 self.canvas.create_text(650, 470, text = "Acceleration", font = MEDIUMFONT)
1067 self.canvas.create_text(700, 435, text = "Displacement", font = MEDIUMFONT)
1068
1069 def returnForces():
1070 window.addScore(2)
1071 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1072 controller.show_frame(forcesPage)
1073
1074class AdditionVectorsCalculationPage(tk.Frame):
1075
1076 def __init__(self, parent, controller):
1077
1078 tk.Frame.__init__(self, parent)
1079 self.title = tk.Label(self, text="Addition of Vectors - Calculation Page", font=LARGEFONT)
1080 self.title.pack(padx=10, pady=10)
1081
1082 self.canvas = Canvas(self, width=800, height=500, bd=2)
1083 self.canvas.pack()
1084
1085 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1086 self.returnButton.pack(ipadx=10)
1087
1088 self.canvas.create_line(25, 150, 425, 20, arrow=LAST, arrowshape="16 20 6")
1089 self.canvas.create_rectangle(400, 125, 425, 150)
1090 self.canvas.create_arc(-50, 100, 100, 200, extent = 25.5)
1091 self.canvas.create_text(110, 135, text = "Θ", font = LABELFONT)
1092
1093 self.canvas.create_line(425, 150, 425,50, arrow=LAST, arrowshape="12 16 4", fill="red")
1094 self.canvas.create_line(425, 50, 425, 20, fill="red")
1095 self.canvas.create_line(25, 150, 375, 150, arrow=LAST, arrowshape="12 16 4", fill="blue")
1096 self.canvas.create_line(375, 150, 425, 150, fill="blue")
1097 self.canvas.create_text(450, 75, text = "a", font = LABELFONT, fill="red")
1098 self.canvas.create_text(225, 170, text = "b", font = LABELFONT, fill="blue")
1099 self.canvas.create_text(225, 50, text = "c", font = LABELFONT)
1100
1101 self.canvas.create_text(625, 75, text = """You will only ever be asked
1102to add vectors by calculation
1103with vectors at right angles
1104
1105Therefore, addition of vectors
1106by calculation simply requires
1107Pythagoras's Theorem""", font = LABELFONT)
1108
1109 self.canvas.create_text(175, 225, text = "PYTHAGORAS", font = LARGEFONT)
1110 self.canvas.create_text(175, 275, text = "a^2 + b^2 = c^2", font = LARGEFONT)
1111 self.canvas.create_text(175, 375, text = "TRIGONOMETRY", font = LARGEFONT)
1112 self.canvas.create_text(175, 425, text = "tan Θ = a / b", font = LARGEFONT)
1113 self.canvas.create_text(550, 325, text = """You may be asked to find the final vector
1114after travelling along two vectors
1115
1116In which case, substitute in 'a' and 'b'
1117and rearrange to find 'c'
1118which will be your vector's magnitude
1119Use trigonometry to find
1120the angle of the vector""", font = LABELFONT)
1121
1122 def returnForces():
1123 window.addScore(2)
1124 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1125 controller.show_frame(forcesPage)
1126
1127class AdditionVectorsScaleDrawingPage(tk.Frame):
1128
1129 def __init__(self, parent, controller):
1130
1131 tk.Frame.__init__(self, parent)
1132 self.title = tk.Label(self, text="Addition of Vectors - Scale Drawing Page", font=LARGEFONT)
1133 self.title.pack(padx=10, pady=10)
1134
1135 self.canvas = Canvas(self, width=800, height=500, bd=2)
1136 self.canvas.pack()
1137
1138 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1139 self.returnButton.pack(ipadx=10)
1140
1141 self.canvas.create_text(50, 10, text = "North", font = LABELFONT)
1142 self.canvas.create_line(50, 200, 50, 20, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1143 self.canvas.create_arc(25, 175, 75, 225, start = 90.0, extent = -45.0)
1144
1145 self.canvas.create_text(175, 10, text = "North", font = LABELFONT)
1146 self.canvas.create_line(175, 75, 175, 20, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1147 self.canvas.create_rectangle(175, 75, 190, 60)
1148
1149 self.canvas.create_line(50, 200, 175, 75, arrow=LAST, arrowshape="16 20 6", fill="red")
1150 self.canvas.create_line(175, 75, 425, 75, arrow=LAST, arrowshape="16 20 6", fill="blue")
1151 self.canvas.create_text(160, 155, text = "10 KM\nVector a", font = LABELFONT, fill="red")
1152 self.canvas.create_text(300, 100, text = "20 KM\nVector b", font = LABELFONT, fill="blue")
1153 self.canvas.create_text(75, 145, text = "45°", font = LABELFONT)
1154 self.canvas.create_text(620, 125, text = """You can be asked to add two vectors
1155by scale drawing
1156
1157The angle between these vectors is
1158NOT limited to right angles
1159
1160You must draw the vectors
1161to scale""", font = LABELFONT)
1162
1163 self.canvas.create_text(400, 240, text = "DRAW TO SCALE - DECLARE YOUR SCALE", font = LARGEFONT)
1164 self.canvas.create_rectangle(15, 285, 145, 315)
1165 self.canvas.create_text(80, 300, text = "1 KM = 1 CM", font = LABELFONT)
1166
1167 self.canvas.create_text(50, 340, text = "North", font = LABELFONT)
1168 self.canvas.create_line(50, 460, 50, 350, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1169 self.canvas.create_arc(25, 435, 75, 485, start = 90.0, extent = -72.5)
1170 self.canvas.create_text(65, 425, text = "Θ", font = LABELFONT)
1171
1172 self.canvas.create_line(50, 460, 175, 335, arrow=LAST, arrowshape="16 20 6", fill="red")
1173 self.canvas.create_line(175, 335, 425, 335, arrow=LAST, arrowshape="16 20 6", fill="blue")
1174 self.canvas.create_text(120, 370, text = "a", font = LABELFONT, fill="red")
1175 self.canvas.create_text(280, 320, text = "b", font = LABELFONT, fill="blue")
1176 self.canvas.create_text(150, 400, text = "10 CM", font = LABELFONT, fill="red")
1177 self.canvas.create_text(280, 355, text = "20 CM", font = LABELFONT, fill="blue")
1178 self.canvas.create_line(50, 460, 425, 335, fill="red")
1179 self.canvas.create_line(50, 460, 425, 335, dash=True, fill="blue")
1180 self.canvas.create_text(300, 420, text = "Resultant Vector", font = MEDIUMFONT, fill="red")
1181
1182 self.canvas.create_text(620, 375, text = "For the magnitude, measure\nthe resultant vector using\na ruler\n\nFor the angle, measure\nΘ with a protractor", font = LABELFONT)
1183
1184 def returnForces():
1185 window.addScore(2)
1186 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1187 controller.show_frame(forcesPage)
1188
1189class ResolutionOfVectorsPage(tk.Frame):
1190
1191 def __init__(self, parent, controller):
1192
1193 tk.Frame.__init__(self, parent)
1194 self.title = tk.Label(self, text="Resolution of Vectors Page", font=LARGEFONT)
1195 self.title.pack(padx=10, pady=10)
1196
1197 self.canvas = Canvas(self, width=800, height=500, bd=2)
1198 self.canvas.pack()
1199
1200 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1201 self.returnButton.pack(ipadx=10)
1202
1203 self.canvas.create_line(50, 150, 450, 20, arrow=LAST, arrowshape="16 20 6")
1204 self.canvas.create_line(50, 150, 50, 20, dash=True, arrow=LAST, arrowshape="12 16 4", fill="red")
1205 self.canvas.create_line(50, 150, 450, 150, dash=True, arrow=LAST, arrowshape="12 16 4", fill="blue")
1206 self.canvas.create_text(250, 50, text = "? ms^-1", font = LABELFONT)
1207 self.canvas.create_text(100, 75, text = "3 ms^-1", font = LABELFONT, fill="red")
1208 self.canvas.create_text(250, 175, text = "4 ms^-1", font = LABELFONT, fill="blue")
1209 self.canvas.create_text(625, 100, text = """Similar to addition of vectors
1210by calculation
1211
1212Every vector has a vertical
1213and horizontal component
1214
1215Simply add the two components
1216in order to find the vector's
1217magnitude""", font = LABELFONT)
1218
1219 self.canvas.create_line(350, 390, 750, 240, arrow=LAST, arrowshape="16 20 6")
1220 self.canvas.create_rectangle(725, 365, 750, 390)
1221 self.canvas.create_arc(300, 340, 400, 440, extent = 20.5)
1222 self.canvas.create_text(420, 377, text = "Θ", font = LABELFONT)
1223
1224 self.canvas.create_line(750, 390, 750, 290, arrow=LAST, arrowshape="12 16 4", fill="red")
1225 self.canvas.create_line(750, 290, 750, 240, fill="red")
1226 self.canvas.create_line(350, 390, 700, 390, arrow=LAST, arrowshape="12 16 4", fill="blue")
1227 self.canvas.create_line(700, 390, 750, 390, fill="blue")
1228 self.canvas.create_text(775, 315, text = "a", font = LABELFONT, fill="red")
1229 self.canvas.create_text(550, 410, text = "b", font = LABELFONT, fill="blue")
1230 self.canvas.create_text(550, 290, text = "c", font = LABELFONT)
1231
1232 self.canvas.create_text(100, 245, text = "a^2 + b^2 = c^2", font = LABELFONT)
1233 self.canvas.create_text(100, 295, text = "3^2 + 4^2 = c^2", font = LABELFONT)
1234 self.canvas.create_text(100, 345, text = "c^2 = 25", font = LABELFONT)
1235 self.canvas.create_text(100, 395, text = "c = 5", font = LABELFONT)
1236 self.canvas.create_text(310, 245, text = "arg Θ = 3 / 4", font = LABELFONT)
1237 self.canvas.create_text(310, 295, text = "Θ = tan^-1( 0.75 )", font = LABELFONT)
1238 self.canvas.create_text(310, 345, text = "Θ ≈ 53°", font = LABELFONT)
1239
1240 self.canvas.create_text(475, 465, text = "Vector Magnitude is 5 ms^-1, Vector Angle is 53°", font = LABELFONT)
1241
1242 def returnForces():
1243 window.addScore(2)
1244 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1245 controller.show_frame(forcesPage)
1246
1247class VectorsInEquilibriumPage(tk.Frame):
1248
1249 def __init__(self, parent, controller):
1250
1251 tk.Frame.__init__(self, parent)
1252 self.title = tk.Label(self, text="Vectors In Equilibrium Page", font=LARGEFONT)
1253 self.title.pack(padx=10, pady=10)
1254
1255 self.canvas = Canvas(self, width=800, height=500, bd=2)
1256 self.canvas.pack()
1257
1258 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1259 self.returnButton.pack(ipadx=10)
1260
1261 self.canvas.create_text(400, 20, text = "F = ma", font = LARGEFONT)
1262 self.canvas.create_text(400, 50, text = "When an object is in equilibrium, the resultant force acting on it is 0", font = LABELFONT)
1263 self.canvas.create_text(400, 70, text = "If F = 0, ma = 0. Assuming the object has mass, when in equilibrium, acceleration = 0", font = LABELFONT)
1264 self.canvas.create_text(400, 90, text = "When acceleration = 0, the object has constant velocity", font = LABELFONT)
1265 # Underline
1266 self.canvas.create_line(160, 102, 640, 102)
1267
1268 # Page divider
1269 self.canvas.create_line(400, 110, 400, 490)
1270
1271 # At rest
1272 self.canvas.create_text(200, 135, text = "Objects in equilibrium - at rest", font = MEDIUMFONT)
1273 self.canvas.create_line(20, 260, 380, 260)
1274 self.canvas.create_rectangle(175, 210, 225, 260)
1275 self.canvas.create_oval(198, 233, 202, 237, fill="black")
1276 self.canvas.create_text(50, 250, text = "Table", font = SMALLFONT)
1277
1278 self.canvas.create_line(200, 160, 200, 235, fill="blue", arrow=FIRST)
1279 self.canvas.create_text(280, 185, text = "Reaction Force", font = LABELFONT, fill="blue")
1280 self.canvas.create_line(200, 235, 200, 310, fill="red", arrow=LAST)
1281 self.canvas.create_text(165, 285, text = "Weight ", font = LABELFONT, fill="red")
1282
1283 self.canvas.create_text(200, 350, text = "The table supports the block by exerting a", font = LABELFONT)
1284 self.canvas.create_text(200, 390, text = "reaction force equal to the blocks weight", font = LABELFONT)
1285 self.canvas.create_text(200, 430, text = "Resultant force is 0 and so its acceleration", font = LABELFONT)
1286 self.canvas.create_text(200, 470, text = "is 0. Therefore it stays at rest", font = LABELFONT)
1287
1288 # Moving
1289 self.canvas.create_text(600, 135, text = "Objects in equilibrium - moving", font = MEDIUMFONT)
1290 self.canvas.create_text(600, 180, text = "Velocity = 30 ms^-1", font = LABELFONT)
1291 self.canvas.create_line(420, 290, 780, 290)
1292 try:
1293 self.Car = tk.PhotoImage(file = "NEACar.png")
1294 self.canvas.create_image(600, 260, image=self.Car)
1295 except:
1296 self.canvas.create_text(600, 260, text = "Car Image not found")
1297 self.canvas.create_line(537, 260, 450, 260, fill="red", arrow=LAST)
1298 self.canvas.create_text(490, 240, text = "Thrust ", font = LABELFONT, fill="red")
1299 self.canvas.create_line(663, 260, 750, 260, fill="blue", arrow=LAST)
1300 self.canvas.create_text(710, 230, text = "Air Resistance", font = LABELFONT, fill="blue")
1301 self.canvas.create_line(635, 290, 750, 290, fill="blue", arrow=LAST)
1302 self.canvas.create_text(690, 305, text = "Friction", font = LABELFONT, fill="blue")
1303
1304 self.canvas.create_text(600, 345, text = "If in equilibrium,", font = LABELFONT)
1305 self.canvas.create_text(600, 375, text = "Thrust = Air Resistance + Friction,", font = LABELFONT)
1306 self.canvas.create_text(600, 405, text = "so the overall force on the car is 0", font = LABELFONT)
1307 self.canvas.create_text(600, 435, text = "So its acceleration is 0", font = LABELFONT)
1308 self.canvas.create_text(600, 465, text = "Therefore it stays at its current velocity", font = LABELFONT)
1309
1310
1311 def returnForces():
1312 window.addScore(2)
1313 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1314 controller.show_frame(forcesPage)
1315
1316class InclinedPlaneVectorsPage(tk.Frame):
1317
1318 def __init__(self, parent, controller):
1319
1320 tk.Frame.__init__(self, parent)
1321 self.title = tk.Label(self, text="Inclined Plane Vectors Page", font=LARGEFONT)
1322 self.title.pack(padx=10, pady=10)
1323
1324 self.canvas = Canvas(self, width=800, height=500, bd=2)
1325 self.canvas.pack()
1326
1327 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1328 self.returnButton.pack(ipadx=10)
1329
1330 self.canvas.create_text(400, 25, text = "When an object is stationary on an inclined plane, 3 forces are acting on it", font = MEDIUMFONT)
1331 self.canvas.create_text(400, 50, text = "1. Weight of the object, acting vertically downwards", font = SMALLFONT)
1332 self.canvas.create_text(400, 75, text = "2. The Support / Reaction force, acting perpendicular to where the weight acts on the plane", font = SMALLFONT)
1333 self.canvas.create_text(400, 100, text = "3. Friction, acting parallel to the plane", font = SMALLFONT)
1334
1335 # Box
1336 self.canvas.create_polygon(120, 225, 160, 215, 150, 175, 110, 185, fill="white", outline="black")
1337 self.canvas.create_oval(133, 198, 137, 202, fill="black")
1338
1339 # Weight arrow
1340 self.canvas.create_line(135, 125, 135, 220, fill="green", arrow=LAST)
1341 self.canvas.create_text(160, 150, text = "Weight", fill="green")
1342
1343 # Support Arrow
1344 self.canvas.create_line(135, 220, 90, 135, fill="blue", arrow=LAST)
1345 self.canvas.create_text(70, 160, text = "Support", fill="blue")
1346
1347 # Friction Arrows
1348 self.canvas.create_line(90, 135, 135, 125, fill="red", arrow=LAST)
1349 self.canvas.create_text(105, 115, text = "Friction", fill="red")
1350 self.canvas.create_line(135, 220, 175, 210, fill="red", arrow=LAST)
1351
1352 # Triangle
1353 self.canvas.create_arc(-40, 190, 80, 310, extent = 14.0)
1354 self.canvas.create_line(20, 250, 500, 250, fill="green")
1355 self.canvas.create_line(500, 250, 420, 150, fill="red")
1356 self.canvas.create_line(20, 250, 420, 150, fill="blue")
1357 self.canvas.create_text(100, 240, text = "Θ", font = SMALLFONT)
1358
1359 self.canvas.create_text(650, 200, text = "As colour coordinated, one can see\nthat the rules of similar triangles\ncan be used\n\nThis allows us to form this triangle ↓", font = SMALLFONT)
1360
1361 # New Triangle
1362 self.canvas.create_arc(210, 390, 330, 510, extent = 14.0)
1363 self.canvas.create_line(270, 450, 750, 450, fill="green", arrow=FIRST)
1364 self.canvas.create_line(750, 450, 670, 350, fill="red", arrow=FIRST)
1365 self.canvas.create_line(270, 450, 670, 350, fill="blue", arrow=LAST)
1366 self.canvas.create_text(350, 440, text = "Θ", font = SMALLFONT)
1367 self.canvas.create_text(550, 465, text = "Weight", fill="green")
1368 self.canvas.create_text(470, 380, text = "Support", fill="blue")
1369 self.canvas.create_text(740, 390, text = "Friction", fill="red")
1370
1371 self.canvas.create_text(320, 350, text = """If asked to find out the angle of the inclined plane, use trigonometry
1372and your values of weight, support and friction to find Θ
1373
1374As the triangle's overall displacement is zero,
1375an unknown weight, support or friction force
1376will be equal to the negative sum of the
1377known values
1378e.g. W = -(S + F)""", font = LABELFONT)
1379 self.canvas.create_text(125, 475, text = "W + S + F = 0", font = MEDIUMFONT)
1380
1381 def returnForces():
1382 window.addScore(2)
1383 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1384 controller.show_frame(forcesPage)
1385
1386# Time Graph Pages #
1387
1388class timeGraphsPage(tk.Frame):
1389
1390 def __init__(self, parent, controller):
1391
1392 tk.Frame.__init__(self, parent)
1393 self.title = tk.Label(self, text="Time Graphs Page", font=LARGEFONT)
1394 self.title.pack(padx=10, pady=10)
1395
1396 self.label = tk.Label(self, text = "Time Graphs:", font = MEDIUMFONT)
1397 self.GradientsButton = tk.Button(self, text = "Gradients", command=lambda: controller.show_frame(GradientsPage))
1398 self.AreaUnderCurveButton = tk.Button(self, text = "Area Under Curve", command=lambda: controller.show_frame(AreaUnderCurvePage))
1399 self.BouncingBallButton = tk.Button(self, text = "Experiment - Bouncing Ball", command=lambda: controller.show_frame(BouncingBallPage))
1400 self.interactiveGraphsButton = tk.Button(self, text = "Interactive Graphs", command=lambda: controller.show_frame(interactiveGraphsPage))
1401
1402 self.label.pack(side="top", fill="x", pady=10)
1403 self.GradientsButton.pack(side="top", pady=15, ipadx=10)
1404 self.AreaUnderCurveButton.pack(side="top", pady=15, ipadx=10)
1405 self.BouncingBallButton.pack(side="top", pady=15, ipadx=10)
1406 self.interactiveGraphsButton.pack(side="top", pady=15, ipadx=10)
1407
1408class GradientsPage(tk.Frame):
1409
1410 def __init__(self, parent, controller):
1411
1412 tk.Frame.__init__(self, parent)
1413 self.title = tk.Label(self, text="Gradients Of Time Graphs Page", font=LARGEFONT)
1414 self.title.grid(row=0 , column=0, columnspan=2, padx=10, pady=10)
1415
1416 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1417 self.distanceCanvas.grid(row=1, column=0)
1418 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1419 self.displacementCanvas.grid(row=1, column=1)
1420 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1421 self.speedCanvas.grid(row=2, column=0)
1422 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1423 self.velocityCanvas.grid(row=2, column=1)
1424 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1425 self.accelerationCanvas.grid(row=3, column=0, columnspan=2)
1426
1427 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1428 self.returnButton.grid(row=4, column=0, ipadx=10)
1429
1430 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1431 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1432
1433 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph",font = MEDIUMFONT, fill="blue")
1434 self.distanceCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1435 self.distanceCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1436 self.distanceCanvas.create_line(50, 135, 195, 40, fill="blue")
1437 self.distanceCanvas.create_text(25, 85, text = "d",font = LABELFONT, fill="blue")
1438 self.distanceCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1439 self.distanceCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="blue")
1440 self.distanceCanvas.create_text(300, 85, text = "= d / t",font = LABELFONT, fill="blue")
1441 self.distanceCanvas.create_text(300, 115, text = "= Speed",font = LABELFONT, fill="blue")
1442
1443 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1444 self.displacementCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1445 self.displacementCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1446 self.displacementCanvas.create_line(50, 135, 195, 40, fill="red")
1447 self.displacementCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="red")
1448 self.displacementCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1449 self.displacementCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1450 self.displacementCanvas.create_text(300, 85, text = "= s / t",font = LABELFONT, fill="red")
1451 self.displacementCanvas.create_text(300, 115, text = "= Velocity",font = LABELFONT, fill="red")
1452
1453 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1454 self.speedCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1455 self.speedCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1456 self.speedCanvas.create_line(50, 135, 195, 40, fill="blue")
1457 self.speedCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="blue")
1458 self.speedCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1459 self.speedCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="blue")
1460 self.speedCanvas.create_text(300, 85, text = "= s / t",font = LABELFONT, fill="blue")
1461 self.speedCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="blue")
1462
1463 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1464 self.velocityCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1465 self.velocityCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1466 self.velocityCanvas.create_line(50, 135, 195, 40, fill="red")
1467 self.velocityCanvas.create_text(25, 85, text = "v",font = LABELFONT, fill="red")
1468 self.velocityCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1469 self.velocityCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1470 self.velocityCanvas.create_text(300, 85, text = "= v / t",font = LABELFONT, fill="red")
1471 self.velocityCanvas.create_text(300, 115, text = "Acceleration",font = LABELFONT, fill="red")
1472
1473 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1474 self.accelerationCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1475 self.accelerationCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1476 self.accelerationCanvas.create_line(50, 135, 195, 40, fill="red")
1477 self.accelerationCanvas.create_text(25, 85, text = "a",font = LABELFONT, fill="red")
1478 self.accelerationCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1479 self.accelerationCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1480 self.accelerationCanvas.create_text(300, 85, text = "= a / t",font = LABELFONT, fill="red")
1481 self.accelerationCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="red")
1482
1483 def returnTimeGraphs():
1484 window.addScore(2)
1485 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1486 controller.show_frame(timeGraphsPage)
1487
1488class AreaUnderCurvePage(tk.Frame):
1489
1490 def __init__(self, parent, controller):
1491
1492 tk.Frame.__init__(self, parent)
1493 self.title = tk.Label(self, text="Area Under Curves Of Time Graphs Page", font=LARGEFONT)
1494 self.title.grid(row=0, column=0, columnspan=2, padx=10, pady=10)
1495
1496 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1497 self.distanceCanvas.grid(row=1, column=0)
1498 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1499 self.displacementCanvas.grid(row=1, column=1)
1500 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1501 self.speedCanvas.grid(row=2, column=0)
1502 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1503 self.velocityCanvas.grid(row=2, column=1)
1504 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1505 self.accelerationCanvas.grid(row=3, column=0, columnspan=2)
1506
1507 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1508 self.returnButton.grid(row=4, column=0, ipadx=10)
1509
1510 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1511 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1512
1513 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph",font = MEDIUMFONT, fill="blue")
1514 self.distanceCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1515 self.distanceCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1516 self.distanceCanvas.create_line(50, 135, 195, 40, fill="blue")
1517 self.distanceCanvas.create_text(25, 85, text = "d",font = LABELFONT, fill="blue")
1518 self.distanceCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1519 self.distanceCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="blue")
1520 self.distanceCanvas.create_text(300, 85, text = "= d x t",font = LABELFONT, fill="blue")
1521 self.distanceCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="blue")
1522
1523 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1524 self.displacementCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1525 self.displacementCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1526 self.displacementCanvas.create_line(50, 135, 195, 40, fill="red")
1527 self.displacementCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="red")
1528 self.displacementCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1529 self.displacementCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1530 self.displacementCanvas.create_text(300, 85, text = "= s x t",font = LABELFONT, fill="red")
1531 self.displacementCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="red")
1532
1533 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1534 self.speedCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1535 self.speedCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1536 self.speedCanvas.create_line(50, 135, 195, 40, fill="blue")
1537 self.speedCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="blue")
1538 self.speedCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1539 self.speedCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="blue")
1540 self.speedCanvas.create_text(300, 85, text = "= s x t",font = LABELFONT, fill="blue")
1541 self.speedCanvas.create_text(300, 115, text = "= Distance",font = LABELFONT, fill="blue")
1542
1543 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1544 self.velocityCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1545 self.velocityCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1546 self.velocityCanvas.create_line(50, 135, 195, 40, fill="red")
1547 self.velocityCanvas.create_text(25, 85, text = "v",font = LABELFONT, fill="red")
1548 self.velocityCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1549 self.velocityCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1550 self.velocityCanvas.create_text(300, 85, text = "= v x t",font = LABELFONT, fill="red")
1551 self.velocityCanvas.create_text(300, 115, text = "Displacement",font = LABELFONT, fill="red")
1552
1553 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1554 self.accelerationCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1555 self.accelerationCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1556 self.accelerationCanvas.create_line(50, 135, 195, 40, fill="red")
1557 self.accelerationCanvas.create_text(25, 85, text = "a",font = LABELFONT, fill="red")
1558 self.accelerationCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1559 self.accelerationCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1560 self.accelerationCanvas.create_text(300, 85, text = "= a x t",font = LABELFONT, fill="red")
1561 self.accelerationCanvas.create_text(300, 115, text = "Velocity",font = LABELFONT, fill="red")
1562
1563 def returnTimeGraphs():
1564 window.addScore(2)
1565 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1566 controller.show_frame(timeGraphsPage)
1567
1568class BouncingBallPage(tk.Frame):
1569
1570 def __init__(self, parent, controller):
1571
1572 tk.Frame.__init__(self, parent)
1573 self.title = tk.Label(self, text="Bouncing Ball Example Page", font=LARGEFONT)
1574 self.title.grid(row=0, column=0, columnspan=2, padx=10, pady=10)
1575
1576 self.bouncingBallCanvas = Canvas(self, width=400, height=160, bd=2)
1577 self.bouncingBallCanvas.grid(row=1, column=0)
1578 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1579 self.distanceCanvas.grid(row=1, column=1)
1580 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1581 self.displacementCanvas.grid(row=2, column=0)
1582 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1583 self.speedCanvas.grid(row=2, column=1)
1584 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1585 self.velocityCanvas.grid(row=3, column=0)
1586 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1587 self.accelerationCanvas.grid(row=3, column=1)
1588
1589 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1590 self.returnButton.grid(row=4, column=0, ipadx=10)
1591
1592 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1593 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1594
1595 self.bouncingBallCanvas.create_oval(50, 10, 90, 50, fill="gray40", outline="gray40")
1596 self.bouncingBallCanvas.create_oval(50, 150, 90, 110, fill="gray40", outline="gray40")
1597 self.bouncingBallCanvas.create_line(60, 50, 60, 110, width=2)
1598 self.bouncingBallCanvas.create_line(80, 50, 80, 110, width=2)
1599 self.bouncingBallCanvas.create_line(60, 50, 45, 65, width=2)
1600 self.bouncingBallCanvas.create_line(80, 110, 95, 95, width=2)
1601 self.bouncingBallCanvas.create_line(20, 152, 130, 152, width = 3)
1602 self.bouncingBallCanvas.create_text(265, 30, text = "Common Exam Question", font = MEDIUMFONT)
1603 self.bouncingBallCanvas.create_text(265, 70, text = "Ball projected upward, whilst", font = SMALLFONT)
1604 self.bouncingBallCanvas.create_text(265, 100, text = "in the air, it is acted on", font = SMALLFONT)
1605 self.bouncingBallCanvas.create_text(265, 130, text = "by gravity only", font = SMALLFONT)
1606
1607 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph", font = MEDIUMFONT, fill="blue")
1608 self.distanceCanvas.create_line(125, 135, 125, 35, width = 3, fill="blue")
1609 self.distanceCanvas.create_line(125, 135, 275, 135, width = 3, fill="blue")
1610 self.distanceCanvas.create_arc(127, 85, 275, 185, start=90, extent=90, style="arc", outline="blue")
1611 self.distanceCanvas.create_arc(127, -15, 275, 85, start=-90, extent=90, style="arc", outline="blue")
1612 self.distanceCanvas.create_text(100, 85, text = "d",font = LABELFONT, fill="blue")
1613 self.distanceCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="blue")
1614
1615 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1616 self.displacementCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1617 self.displacementCanvas.create_line(125, 135, 275, 135, width = 3, fill="red")
1618 self.displacementCanvas.create_arc(127, 35, 275, 235, extent=180, style="arc", outline="red")
1619 self.displacementCanvas.create_text(100, 85, text = "s",font = LABELFONT, fill="red")
1620 self.displacementCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="red")
1621
1622 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1623 self.speedCanvas.create_line(125, 135, 125, 35, width = 3, fill="blue")
1624 self.speedCanvas.create_line(125, 135, 275, 135, width = 3, fill="blue")
1625 self.speedCanvas.create_arc(127, -60, 275, 133, extent=-180, style="arc", outline="blue")
1626 self.speedCanvas.create_text(100, 85, text = "s",font = LABELFONT, fill="blue")
1627 self.speedCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="blue")
1628
1629 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1630 self.velocityCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1631 self.velocityCanvas.create_line(125, 85, 275, 85, width = 3, fill="red")
1632 self.velocityCanvas.create_line(125, 35, 275, 135, fill="red")
1633 self.velocityCanvas.create_text(100, 85, text = "v",font = LABELFONT, fill="red")
1634 self.velocityCanvas.create_text(295, 85, text = "t",font = LABELFONT, fill="red")
1635
1636 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1637 self.accelerationCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1638 self.accelerationCanvas.create_line(125, 85, 275, 85, width = 3, fill="red")
1639 self.accelerationCanvas.create_line(125, 50, 275, 50, fill="red")
1640 self.accelerationCanvas.create_text(100, 85, text = "a",font = LABELFONT, fill="red")
1641 self.accelerationCanvas.create_text(295, 85, text = "t",font = LABELFONT, fill="red")
1642 self.accelerationCanvas.create_text(329, 49, text = "= 9.8 ms^-1",font = SMALLFONT, fill="red")
1643
1644 def returnTimeGraphs():
1645 window.addScore(2)
1646 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1647 controller.show_frame(timeGraphsPage)
1648
1649class interactiveGraphsPage(tk.Frame):
1650
1651 def __init__(self, parent, controller):
1652
1653 self.interactiveGraphsPage = True
1654
1655 tk.Frame.__init__(self, parent)
1656 self.title = tk.Label(self, text="Interactive Graphs Page", font=LARGEFONT)
1657 self.title.pack(side="top", padx=10, pady=10)
1658
1659 # Draws the backend graph onto the tkinter page
1660 canvas = FigureCanvasTkAgg(f, self)
1661 canvas.show()
1662 canvas.get_tk_widget().pack(side="top")
1663
1664 # Draws the matplotlib's navigation tool onto the canvas
1665 toolbar = NavigationToolbar2TkAgg(canvas, self)
1666 toolbar.update()
1667 canvas._tkcanvas.pack(side="top")
1668
1669 # Creates the graph controller window
1670 self.graphControllerDisplayButton = tk.Button(self, text = "Click for Graph Controller", command=lambda: self.createGraphController())
1671 self.graphControllerDisplayButton.pack(side="left", ipadx=10, padx=80, pady=1)
1672
1673 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1674 self.returnButton.pack(side="left", ipadx=10, padx=61, pady=1)
1675
1676 def returnTimeGraphs():
1677 window.addScore(5)
1678 messagebox.showinfo("Score Updated","You earned 5 points for visiting this page", icon = "info")
1679 controller.show_frame(timeGraphsPage)
1680
1681 def onclick(event): # Called when graph canvas is clicked
1682 try: # If user clicks outside of the graph, it causes an error. If not, call the onClickFunction
1683 onclickFunction(event)
1684 except: # If they do click outside the graph, nothing should happen so just pass
1685 pass
1686
1687 def onclickFunction(event): # Called when graph is clicked
1688 xValue = event.xdata # Retrieves the x coordinate of the user's click
1689
1690 if xValue % 1 != 0: # Checks whether x coordinate has a gradient, if yes....
1691 yValue = event.ydata # Retrieves the y coordinate of the user's click
1692
1693 lowerX = math.floor(xValue) #
1694 upperX = math.ceil(xValue)
1695
1696 try:
1697 lowerPosition = animationState.xList.index(lowerX)
1698 lowerY = animationState.yList[lowerPosition]
1699
1700 upperPosition = animationState.xList.index(upperX)
1701 upperY = animationState.yList[upperPosition]
1702
1703 gradient = (upperY - lowerY)/(upperX - lowerX)
1704
1705 units = animationState.gradientUnits()
1706
1707 messagebox.showinfo("Gradient", "Gradient = {0} {1}".format(gradient, units), icon="info")
1708 except:
1709 messagebox.showinfo("Error", "Not enough data\n\nClick somewhere where a line is present", icon="warning")
1710
1711 else: # Called if user clicks where x coordinate is an integer
1712 messagebox.showinfo("Error", "You have clicked where the line changes gradient\n\nTry again", icon="warning")
1713
1714
1715 f.canvas.callbacks.connect("button_press_event", onclick) # When the user clicks on the graph canvas, call the onlick function
1716
1717 def createGraphController(self): # Creates the graph controller window, if its already made, focus on the window
1718 try:
1719 self.graphController.lift()
1720 self.graphController.focus_force()
1721 except:
1722 self.graphController = GraphController()
1723 self.graphController.protocol("WM_DELETE_WINDOW", self.closeGraphController)
1724 self.graphController.mainloop()
1725
1726 def closeGraphController(self): # Pauses the backend of the graph, and destroys the graph controller window
1727 animationState.play = False
1728 self.graphController.destroy()
1729
1730class GraphController(tk.Tk):
1731
1732 def __init__(self, *args, **kwargs):
1733
1734 self.present = True
1735
1736 tk.Tk.__init__(self, *args, **kwargs)
1737
1738 tk.Tk.iconbitmap(self, default="NEALogo.ico")
1739 tk.Tk.wm_title(self, "Graph Controller")
1740
1741 self.geometry("480x350")
1742 self.resizable(width=False,height=False)
1743
1744 self.currentState = tk.Label(self, text = "Graph is: Paused")
1745 self.currentState.grid(row=0, column=0, padx=30, pady=20)
1746
1747 self.playPauseButton = tk.Button(self, text="Play/Pause", command=lambda: self.playPause())
1748 self.playPauseButton.grid(row=0, column=1, padx=30, pady=20)
1749
1750 self.line1 = tk.Label(self, text = "---------------------------------------------------------")
1751 self.line1.grid(row=1, column=0, columnspan=2)
1752
1753 self.selectGraphType = tk.Label(self, text = "Select Graph Type: (Default is Distance-Time Graph)")
1754 self.selectGraphType.grid(row=2, column=0, columnspan=2, padx=30, pady=15)
1755
1756 self.distanceButton = tk.Button(self, text = "Distance-Time Graph", command=lambda: self.changeLabels("distance /m"))
1757 self.distanceButton.grid(row=3, column=0, padx=10, pady=10)
1758
1759 self.displacementButton = tk.Button(self, text = "Displacement-Time Graph", command=lambda: self.changeLabels("displacement /m"))
1760 self.displacementButton.grid(row=3, column=1, padx=10, pady=10)
1761
1762 self.speedButton = tk.Button(self, text = "Speed-Time Graph", command=lambda: self.changeLabels("speed /ms^-1"))
1763 self.speedButton.grid(row=4, column=0, padx=10, pady=10)
1764
1765 self.velocityButton = tk.Button(self, text = "Velocity-Time Graph", command=lambda: self.changeLabels("velocity /ms^-1"))
1766 self.velocityButton.grid(row=4, column=1, padx=10, pady=10)
1767
1768 self.accelerationButton = tk.Button(self, text = "Acceleration-Time Graph", command=lambda: self.changeLabels("acceleration /ms^-2"))
1769 self.accelerationButton.grid(row=5, column=0, columnspan=2, padx=10, pady=10)
1770
1771 self.line2 = tk.Label(self, text = "---------------------------------------------------------")
1772 self.line2.grid(row=6, column=0, columnspan=2)
1773
1774 self.areaUnderGraphButton = tk.Button(self, text = "Area Under Graph", command=lambda: self.areaUnderGraph())
1775 self.areaUnderGraphButton.grid(row=7, column=0, padx=10, pady=10)
1776
1777 self.clearGraphButton = tk.Button(self, text = "Clear Graph", command=lambda: self.clearGraph())
1778 self.clearGraphButton.grid(row=7, column=1, padx=10, pady=10)
1779
1780 self.line3 = tk.Label(self, text = """|
1781|
1782|
1783|
1784|
1785|
1786|
1787|
1788|
1789|
1790|
1791|
1792|
1793|
1794|
1795|
1796|
1797|
1798|
1799|
1800|""")
1801 self.line3.grid(row=0, column=2, rowspan=8, padx=5)
1802
1803 self.gradientLabel = tk.Label(self, text = "Slide to change\ngraph gradient")
1804 self.gradientLabel.grid(row=0, column=3, sticky=N, padx=13, pady=15)
1805
1806 self.gradientScale = tk.Scale(self, from_=10, to=-10, length=250)
1807 self.gradientScale.grid(row=1, column=3, rowspan=7, sticky=W, padx=25)
1808
1809 self.scaleLabel = tk.Label(self, text = "- 0")
1810 self.scaleLabel.place(x=423, y=193)
1811
1812 def playPause(self): # Pauses or runs the backend of the graph
1813 animationState.changeState()
1814 if animationState.play == True:
1815 self.currentState.configure(text="Graph is: Running") # Changes the label on the graph controller as well
1816 elif animationState.play == False:
1817 self.currentState.configure(text="Graph is: Paused")
1818
1819 def changeLabels(self, graphType): # Alters the graph's axis labels to the appropriate graph
1820 self.clearGraph()
1821 titlePart = graphType.capitalize()
1822 titlePart2 = titlePart.split(" ")[0]
1823 title = "{0}-Time Graph".format(titlePart2)
1824 animationState.title = title
1825 animationState.ylabel = graphType
1826 animationState.xlabel = "time /s"
1827
1828 def changeGradient(self, changeAmount):
1829 animationState.currentGradient += changeAmount
1830
1831 def areaUnderGraph(self):
1832 area = areaUnderCurve(animationState.xList, animationState.yList)
1833 if area == "N/A": # Returned from areaUnderCurve function, means not enough data was given
1834 pass
1835 else:
1836 units = animationState.areaUnits()
1837 messagebox.showinfo("Area Under Curve", "Area Under Curve = {0} {1}".format(area, units), icon="info")
1838
1839 def clearGraph(self): # Clears the graph, resets the lists of coordinates and resets the xCounter
1840 a.clear()
1841 animationState.xList = [0]
1842 animationState.yList = [0]
1843 animationState.xCounter = 1
1844
1845# SUVAT Revision Pages #
1846
1847class SUVATRevisionPage(tk.Frame):
1848
1849 def __init__(self, parent, controller):
1850
1851 tk.Frame.__init__(self, parent)
1852 self.title = tk.Label(self, text="SUVAT Revision Page", font=LARGEFONT)
1853 self.title.pack(side="top", fill="x", pady=30)
1854
1855 self.SUVATCheckerButton = tk.Button(self, text = "SUVAT Checker", command=lambda: controller.show_frame(SUVATCheckerPage),height=2,width=20)
1856 self.label1 = tk.Label(self, text = "or ")
1857 self.SUVATSolverButton = tk.Button(self, text = "SUVAT Solver", command=lambda: controller.show_frame(SUVATSolverPage),height=2,width=20)
1858
1859 self.SUVATCheckerButton.pack(side="top", pady=5)
1860 self.label1.pack(side="top", fill="x", pady=5)
1861 self.SUVATSolverButton.pack(side="top", pady=5)
1862
1863class SUVATCheckerPage(tk.Frame):
1864
1865 def __init__(self, parent, controller):
1866
1867 tk.Frame.__init__(self, parent)
1868 self.title = tk.Label(self, text="SUVAT Checker Page", font=LARGEFONT)
1869 self.title.pack(padx=10, pady=10)
1870
1871 # Instructions explaining to the user what to enter into each entry field
1872 self.instructions = tk.Label(self, text = """You should have 3 known values, and an unknown value that you want to find
1873For each of the boxes below, either:
18741. The value is known so type it into the box
18752. The value is unknown and the value you want to find, type in 'A'
18763. The value is unknown and not the value you want to find, type in 'X'
1877
1878Enter your answer in the box by the check button (Please enter your answer to 2 decimal places. No units!)
1879A correct answer will earn you 10 points
1880An incorrect answer will lose you 1 point
1881""")
1882
1883 self.displacementText = tk.Label(self, text = "Displacement:")
1884 self.displacementInput = tk.Entry(self, justify="center", width=30)
1885
1886 self.initialVelocityText = tk.Label(self, text = "Initial Velocity:")
1887 self.initialVelocityInput = tk.Entry(self, justify="center", width=30)
1888
1889 self.finalVelocityText = tk.Label(self, text = "Final Velocity:")
1890 self.finalVelocityInput = tk.Entry(self, justify="center", width=30)
1891
1892 self.accelerationText = tk.Label(self, text = "Acceleration:")
1893 self.accelerationInput = tk.Entry(self, justify="center", width=30)
1894
1895 self.timeTakenText = tk.Label(self, text = "Time Taken:")
1896 self.timeTakenInput = tk.Entry(self, justify="center", width=30)
1897
1898 self.userAnswerText = tk.Label(self, text = "Your Answer: (Please enter your answer to 2 decimal places. No units!)\nSeparate dual answers with a single comma, no spaces!")
1899 self.userAnswerInput = tk.Entry(self, justify="center", width=30)
1900 self.submitButton = tk.Button(self,text = "Check", command=lambda: Solve())
1901
1902 self.instructions.pack(side="top", fill="x")
1903
1904 self.displacementText.pack(side="top", fill="x", padx=20, pady=5)
1905 self.displacementInput.pack(side="top")
1906 self.displacementInput.focus()
1907
1908 self.initialVelocityText.pack(side="top", fill="x", padx=20, pady=5)
1909 self.initialVelocityInput.pack(side="top")
1910
1911 self.finalVelocityText.pack(side="top", fill="x", padx=20, pady=5)
1912 self.finalVelocityInput.pack(side="top")
1913
1914 self.accelerationText.pack(side="top", fill="x", padx=20, pady=5)
1915 self.accelerationInput.pack(side="top")
1916
1917 self.timeTakenText.pack(side="top", fill="x", padx=20, pady=5)
1918 self.timeTakenInput.pack(side="top")
1919
1920 self.userAnswerText.pack(side="top", fill="x", padx=20, pady=5)
1921 self.userAnswerInput.pack(side="top", pady=5)
1922 self.submitButton.pack(side="bottom", fill="y", padx=20, pady=5)
1923
1924 def Solve():
1925
1926 def deleteEntryFields(): # Clears all the entry fields
1927 self.displacementInput.delete(0,"end")
1928 self.initialVelocityInput.delete(0,"end")
1929 self.finalVelocityInput.delete(0,"end")
1930 self.accelerationInput.delete(0,"end")
1931 self.timeTakenInput.delete(0,"end")
1932 self.userAnswerInput.delete(0,"end")
1933
1934 def response(message1, message2, score, add): # Displays answer to user
1935 messagebox.showinfo("{0}".format(message1),"{0}".format(message2), icon = "info")
1936 if add:
1937 window.addScore(score)
1938 messagebox.showinfo("Score Updated","You earnt {0} points for beating PHYSICS".format(score), icon = "info")
1939 else:
1940 window.addScore(-score)
1941 messagebox.showinfo("Score Updated","You lost {0} points for losing to PHYSICS".format(score), icon = "info")
1942
1943 try:
1944 s = self.displacementInput.get().lower()
1945 except:
1946 s = self.displacementInput.get()
1947 try:
1948 u = self.initialVelocityInput.get().lower()
1949 except:
1950 u = self.initialVelocityInput.get()
1951 try:
1952 v = self.finalVelocityInput.get().lower()
1953 except:
1954 v = self.finalVelocityInput.get()
1955 try:
1956 a = self.accelerationInput.get().lower()
1957 except:
1958 a = self.accelerationInput.get()
1959 try:
1960 t = self.timeTakenInput.get().lower()
1961 except:
1962 t = self.timeTakenInput.get()
1963
1964 suvat = [s,u,v,a,t]
1965
1966 Xcount = 0
1967 Acount = 0
1968 numberCount = 0
1969 for each in suvat:
1970 if each.lower() == "x": # Increments if an x is found
1971 Xcount += 1
1972 elif each.lower() == "a":
1973 Acount += 1
1974
1975 if each.isdigit(): # Used to ensure 3 values are numbers and stops the program crashing from attempting to convert letters into float numbers
1976 numberCount += 1
1977 elif "-" in each or "." in each: # If there is a negative or decimal number, still increment the numberCount
1978 numberCount += 1
1979
1980 if "" in suvat:
1981 messagebox.showinfo("Error!","Leave no fields blank", icon="warning")
1982 elif self.userAnswerInput.get() == "":
1983 messagebox.showinfo("Error!","Enter an answer to check", icon="warning")
1984 elif Acount == 0:
1985 messagebox.showinfo("Error!","No value given as 'A'", icon="warning")
1986 elif Acount > 1:
1987 messagebox.showinfo("Error!","Only one value can be given as 'A'", icon="warning")
1988 elif Xcount == 0:
1989 messagebox.showinfo("Error!","No value given as 'X'", icon="warning")
1990 elif Xcount > 1:
1991 messagebox.showinfo("Error!","Only one value can be given as 'X'", icon="warning")
1992 elif numberCount != 3:
1993 messagebox.showinfo("Error!","Three values entered must be numbers", icon="warning")
1994 else:
1995 Solver = SUVAT(s,u,v,a,t,shortAnswer=True) # Calls the Solving algorithm, passing through the parameters given by the user
1996 # shortAnswer is True, so only the value is returned, no text
1997 answer = Solver.initiate() # Returns the answer(s) from the SUVAT equations
1998
1999 if answer == "None": # If an error occurs in the mathematics, 'None' is returned and this stops an error occuring
2000 messagebox.showinfo("Error","Invalid details inputted, try again", icon = "warning")
2001
2002 else:
2003 if answer == "zeroDivision error":
2004 messagebox.showinfo("Error","Invalid details inputted, resulting in a calculation involving a division by 0\n\nCheck your numbers", icon = "warning")
2005
2006 else:
2007 userAnswer = self.userAnswerInput.get()
2008
2009 if "," in userAnswer: # Comma suggests the answer has two parts, and so continues onto code that compares two answers
2010 userAnswer1, userAnswer2 = userAnswer.split(",") # Splits the answers into two variables
2011 answer1, answer2 = "{:.2f}".format(answer[0]), "{:.2f}".format(answer[1]) # Converts answers from SUVAT() into 2 decimal place variables
2012 userAnswer1, userAnswer2 = "{:.2f}".format(float(userAnswer1)), "{:.2f}".format(float(userAnswer2))
2013
2014 if userAnswer1 == answer1:
2015 if userAnswer2 == answer2:
2016 response("Correct!!", "You got both answers correct", 15, True)
2017 deleteEntryFields()
2018
2019 else: # First correct, second incorrect
2020 response("Incorrect!!", "Your second answer was wrong, try again", 1, False)
2021 self.userAnswerInput.delete(",","end")
2022
2023 else: # First answer is incorrect
2024 if userAnswer2 == answer2: #
2025 response("Incorrect!!", "Your first answer was wrong, try again", 1, False)
2026 self.userAnswerInput.delete(0,",")
2027
2028 else:
2029 if userAnswer1 == answer2:# If user enters dual answers in wrong order, compare opposite answers
2030 if userAnswer2 == answer1:
2031 response("Correct!!", "You got both answers correct", 15, True)
2032 deleteEntryFields()
2033
2034 else: # First answer correct, second incorrect
2035 response("Incorrect!!", "Your second answer was wrong, try again", 1, False)
2036 self.userAnswerInput.delete(0,",")
2037
2038 elif userAnswer2 == answer1:
2039 response("Incorrect!!", "Your first answer was wrong, try again", 1, False)
2040 self.userAnswerInput.delete(",","end")
2041
2042 else:
2043 response("Incorrect!!", "You got both answers wrong, try again", 2, False)
2044 self.userAnswerInput.delete(0,"end")
2045
2046 else: # No comma so only one part, so only us the first element in array 'answer'
2047 answer = "{0:.2f}".format(answer[0])
2048 if userAnswer == answer:
2049 response("Correct!!", "You got the right answer!!", 10, True)
2050 deleteEntryFields()
2051
2052 else:
2053 response("Incorrect!!", "You entered the wrong answer", 1, False)
2054 self.userAnswerInput.delete(0,"end")
2055
2056class SUVATSolverPage(tk.Frame):
2057
2058 def __init__(self, parent, controller):
2059
2060 tk.Frame.__init__(self, parent)
2061 self.title = tk.Label(self, text="SUVAT Solver Page", font=LARGEFONT)
2062 self.title.pack(padx=10, pady=10)
2063
2064 self.instructions = tk.Label(self, text = """You should have 3 known values, and an unknown value that you want to find
2065For each of the boxes below, either:
20661. The value is known so type it into the box
20672. The value is unknown and the value you want to find, type in 'A'
20683. The value is unknown and not the value you want to find, type in 'X'
2069
2070Warning!!! Every answer you seek from this tool with remove 5 points from your account!
2071""")
2072
2073 self.displacementText = tk.Label(self, text = "Displacement:")
2074 self.displacementInput = tk.Entry(self, justify="center", width=30)
2075
2076 self.initialVelocityText = tk.Label(self, text = "Initial Velocity:")
2077 self.initialVelocityInput = tk.Entry(self, justify="center", width=30)
2078
2079 self.finalVelocityText = tk.Label(self, text = "Final Velocity:")
2080 self.finalVelocityInput = tk.Entry(self, justify="center", width=30)
2081
2082 self.accelerationText = tk.Label(self, text = "Acceleration:")
2083 self.accelerationInput = tk.Entry(self, justify="center", width=30)
2084
2085 self.timeTakenText = tk.Label(self, text = "Time Taken:")
2086 self.timeTakenInput = tk.Entry(self, justify="center", width=30)
2087
2088 self.submitButton = tk.Button(self,text = "Solve", command=lambda: Solve())
2089
2090 self.instructions.pack(side="top", fill="x", pady=15)
2091
2092 self.displacementText.pack(side="top", fill="x", padx=20, pady=5)
2093 self.displacementInput.pack(side="top")
2094 self.displacementInput.focus()
2095
2096 self.initialVelocityText.pack(side="top", fill="x", padx=20, pady=5)
2097 self.initialVelocityInput.pack(side="top")
2098
2099 self.finalVelocityText.pack(side="top", fill="x", padx=20, pady=5)
2100 self.finalVelocityInput.pack(side="top")
2101
2102 self.accelerationText.pack(side="top", fill="x", padx=20, pady=5)
2103 self.accelerationInput.pack(side="top")
2104
2105 self.timeTakenText.pack(side="top", fill="x", padx=20, pady=5)
2106 self.timeTakenInput.pack(side="top")
2107
2108 self.submitButton.pack(side="top", fill="y", padx=20, pady=10)
2109
2110 def Solve():
2111
2112 # Validation Statements
2113
2114 try:
2115 s = self.displacementInput.get().lower()
2116 except:
2117 s = self.displacementInput.get()
2118 try:
2119 u = self.initialVelocityInput.get().lower()
2120 except:
2121 u = self.initialVelocityInput.get()
2122 try:
2123 v = self.finalVelocityInput.get().lower()
2124 except:
2125 v = self.finalVelocityInput.get()
2126 try:
2127 a = self.accelerationInput.get().lower()
2128 except:
2129 a = self.accelerationInput.get()
2130 try:
2131 t = self.timeTakenInput.get().lower()
2132 except:
2133 t = self.timeTakenInput.get()
2134
2135 suvat = [s,u,v,a,t]
2136
2137 Xcount = 0
2138 Acount = 0
2139 numberCount = 0
2140 for each in suvat:
2141 if each.lower() == "x":
2142 Xcount += 1
2143 elif each.lower() == "a":
2144 Acount += 1
2145
2146 if each.isdigit(): # Used to ensure 3 values are numbers and stops the program crashing from attempting to convert letters into float numbers
2147 numberCount += 1
2148 elif "-" in each or "." in each:
2149 numberCount += 1
2150
2151 if "" in suvat:
2152 messagebox.showinfo("Error!","Leave no fields blank", icon="warning")
2153 elif Acount == 0:
2154 messagebox.showinfo("Error!","No value given as 'A'", icon="warning")
2155 elif Acount > 1:
2156 messagebox.showinfo("Error!","Only one value can be given as 'A'", icon="warning")
2157 elif Xcount == 0:
2158 messagebox.showinfo("Error!","No value given as 'X'", icon="warning")
2159 elif Xcount > 1:
2160 messagebox.showinfo("Error!","Only one value can be given as 'X'", icon="warning")
2161 elif numberCount != 3:
2162 messagebox.showinfo("Error!","Three values entered must be numbers", icon="warning")
2163 else:
2164 Solver = SUVAT(s,u,v,a,t,shortAnswer=False) # Calls the Solving algorithm, passing through the parameters given by the user, shortAnswer=False means worded solution returned
2165 answer = Solver.initiate()
2166 if answer == "None":
2167 messagebox.showinfo("Error","Invalid values inputted, try again", icon = "warning")
2168
2169 else:
2170 if answer == "zeroDivision error":
2171 messagebox.showinfo("Error","Invalid details inputted, resulting in a calculation involving a division by 0\n\nCheck your numbers", icon = "warning")
2172
2173 else:
2174 messagebox.showinfo("Answer","{0}".format(answer), icon = "info")
2175 window.addScore(-5)
2176 messagebox.showinfo("Score Updated","You lost 5 points for cheating PHYSICS by just wanting an answer", icon = "info")
2177 deleteEntryFields()
2178
2179
2180 def deleteEntryFields():
2181 self.displacementInput.delete(0,"end")
2182 self.initialVelocityInput.delete(0,"end")
2183 self.finalVelocityInput.delete(0,"end")
2184 self.accelerationInput.delete(0,"end")
2185 self.timeTakenInput.delete(0,"end")
2186
2187#############################
2188
2189
2190
2191
2192
2193##### Tkinter Runtime #####
2194
2195def exitProgram():
2196 if messagebox.askokcancel("Quit", "Do you wish to quit?"):
2197 window.logout()
2198 try: # If the graph controller is still open, destroy it
2199 window.frames[interactiveGraphsPage].graphController.destroy()
2200 except:
2201 pass
2202 window.destroy()
2203
2204
2205if runProgram: # If any python modules are missing, the Tkinter window will not be called and the program will cease
2206 window = GUI()
2207 ani = animation.FuncAnimation(f,animate, interval=500)
2208 window.protocol("WM_DELETE_WINDOW", exitProgram) # If 'x' in top right corner of GUI window pressed, call exitProgram()
2209 window.mainloop()
2210
2211###########################