· 9 years ago · Apr 08, 2017, 10:26 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
508 # Distance and Speed graphs are scalar and cannot be negative
509 # This if statement stops the graph from updating if it is going to become zero and it is one of these scalar graphs
510 nextYCoord = yList[int((animationState.xCounter)-1)] + window.frames[interactiveGraphsPage].graphController.gradientScale.get()
511 if nextYCoord < 0:
512 if "distance" in animationState.ylabel or "speed" in animationState.ylabel:
513 animationState.changeState()
514 window.frames[interactiveGraphsPage].graphController.currentState.configure(text="Graph is: Paused")
515 messagebox.showinfo("Warning","The next coordinate would be negative\n\nGraphs of this type cannot be negative\n\nDistance and Speed are Scalar Quantities\n\nScalar Quantities cannot be negative", icon="warning")
516 window.frames[interactiveGraphsPage].graphController.gradientScale.set(0)
517 else:
518 yList.append(yList[int((animationState.xCounter)-1)] + window.frames[interactiveGraphsPage].graphController.gradientScale.get())
519 xList.append(animationState.xCounter)
520
521 a.clear()
522 a.plot(xList, yList)
523 a.axhline(linewidth=0.5, color="k")
524 a.axvline(linewidth=0.5, color="k")
525 a.set_title(animationState.title)
526 a.set_ylabel(animationState.ylabel)
527 a.set_xlabel(animationState.xlabel)
528 animationState.xCounter = animationState.xCounter + 1
529
530 else: # If the graph can continue, it updates the lists of coordinates and creates the new graph
531 yList.append(yList[int((animationState.xCounter)-1)] + window.frames[interactiveGraphsPage].graphController.gradientScale.get())
532 xList.append(animationState.xCounter)
533
534 a.clear()
535 a.plot(xList, yList)
536 a.axhline(linewidth=0.5, color="k")
537 a.axvline(linewidth=0.5, color="k")
538 a.set_title(animationState.title)
539 a.set_ylabel(animationState.ylabel)
540 a.set_xlabel(animationState.xlabel)
541 animationState.xCounter = animationState.xCounter + 1
542
543#########################################
544
545
546
547
548
549##### GUI Classes #####
550
551# Another example of composition
552# GUI class uses Tkinter's Frames as pages in the Tkinter window
553# Each of these Frames are objects instantiated from the child classes
554class GUI(tk.Tk):
555
556 def __init__(self, *args, **kwargs): # Allows a variable amount of keyworded and non-keyworded arguements to be passed into the object
557
558 self.activeUser = "" # Stores the Username of a current user
559
560 tk.Tk.__init__(self, *args, **kwargs)
561
562 tk.Tk.iconbitmap(self, default="NEALogo.ico") # Displays telescope icon to be displayed in left-hand corner of the GUI window
563 tk.Tk.wm_title(self, "Physics Learning Aid") # Displays on the header of th GUI window
564
565 container = tk.Frame(self) # Container holds all elements of the frames and the toolbar
566 container.pack(side="top", fill="both", expand = True)
567 container.grid_rowconfigure(0, weight=1) # Weight attribute added to allow the rows and colunmns to display properly
568 container.grid_columnconfigure(0, weight=1)
569
570 menubar = tk.Menu(container) # Creates the toolbar where students can easily access different pages
571 self.filemenu = tk.Menu(menubar, tearoff=0)
572 self.filemenu.add_command(label="Home", state="disable", command = lambda: self.show_frame(homePage)) # Pages disabled until a user logs in
573 self.filemenu.add_command(label="1: Forces", state="disable", command = lambda: self.show_frame(forcesPage))
574 self.filemenu.add_command(label="2: Time Graphs", state="disable", command = lambda: self.show_frame(timeGraphsPage))
575 self.filemenu.add_command(label="3. SUVAT Revision", state="disable", command = lambda: self.show_frame(SUVATRevisionPage))
576 self.filemenu.add_command(label="Account", command = lambda: self.show_frame(accountPage))
577
578 helpmenu = tk.Menu(self.filemenu, tearoff=0) # Dropdown menu
579 helpmenu.add_command(label="General Help", command=self.helpme)
580 helpmenu.add_command(label="Application Help", command=self.helpme)
581 helpmenu.add_command(label="SUVAT Solver Help", command=self.helpme)
582 self.filemenu.add_cascade(label="Help", menu=helpmenu)
583
584 tk.Tk.config(self, menu=self.filemenu)
585
586 self.frames = {}
587
588 # Creates each individual page as an object is instanciated from the page classes
589 for page in (startPage, accountPage, changePasswordPage, loginPage, signupPage, adminLoginPage, adminPage, homePage, forcesPage, ScalarsVectorsPage, AdditionVectorsCalculationPage,
590 AdditionVectorsScaleDrawingPage, ResolutionOfVectorsPage, InclinedPlaneVectorsPage, VectorsInEquilibriumPage, timeGraphsPage, GradientsPage, AreaUnderCurvePage, BouncingBallPage,
591 interactiveGraphsPage, SUVATRevisionPage, SUVATCheckerPage, SUVATSolverPage):
592
593 frame = page(container, self)
594
595 self.frames[page] = frame
596
597 frame.grid(row=0, column=0, sticky="nsew")
598
599 self.centreScreen(WIDTH, HEIGHT)
600
601 def helpme(self):
602 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",
603 "I'm in the Bahamas on holiday right now\n\nLEAVE\n\nME\n\nALONE"]
604 helpChoice = random.randrange(len(helpList))
605 helpComment = helpList[helpChoice-1]
606 messagebox.showinfo("Help Bot","Hi, you have requested help\n\n{}".format(helpComment), icon="info")
607
608 def enableWidgets(self):
609 self.filemenu.entryconfig("Home", state="normal")
610 self.filemenu.entryconfig("1: Forces", state="normal")
611 self.filemenu.entryconfig("2: Time Graphs", state="normal")
612 self.filemenu.entryconfig("3. SUVAT Revision", state="normal")
613
614 def disableWidgets(self):
615 self.filemenu.entryconfig("Home", state="disable")
616 self.filemenu.entryconfig("1: Forces", state="disable")
617 self.filemenu.entryconfig("2: Time Graphs", state="disable")
618 self.filemenu.entryconfig("3. SUVAT Revision", state="disable")
619
620 def addScore(self, score):
621 if window.activeUser != "Admin" and window.activeUser != "":
622 USERNAMES[self.activeUser][1] += score
623
624 def centreScreen(self, w, h): # Uses screen dimensions to centre the GUI window
625
626 ws = self.winfo_screenwidth()
627 hs = self.winfo_screenheight()
628
629 # Finds the top left coordinate that the GUI will be in order to be centered
630 x = (ws / 2) - (w / 2)
631 y = (hs / 2) - (w / 2)
632
633 # Align window with the top left coordinate
634 self.geometry("%dx%d+%d+%d" % (w, h, x, y)) # takes parameters as 'wxh+x+y'
635 self.resizable(width=False, height=False)
636
637 # Sets page that program will start with
638 self.show_frame(startPage)
639
640 def logout(self):
641 if self.activeUser != "" and self.activeUser != ADMIN[0]:
642 date = getCurrentDate()
643 USERNAMES[self.activeUser][2] = date
644 self.activeUser = ""
645 self.disableWidgets()
646 messagebox.showinfo("Logged out", "You have been logged out successfully")
647 saveUsers()
648 self.show_frame(startPage)
649
650 def show_frame(self, cont):
651
652 frame = self.frames[cont]
653
654 if cont.__name__ != "interactiveGraphsPage": # Checks if the new page to be displayed is not called 'interactiveGraphsPage'
655 try:
656 self.frames[interactiveGraphsPage].graphController.destroy() # If it is, try to destroy the graph controller window
657 except: # The except statement will run if the graph controller window is not open
658 pass # In which case, ignore and continue
659
660 frame.tkraise() # When called it raises the given page to the top of the GUI
661
662### Start of Page Classes ###
663
664class startPage(tk.Frame):
665
666 def __init__(self, parent, controller):
667
668 tk.Frame.__init__(self, parent)
669 self.title = tk.Label(self, text="Start Page", font=LARGEFONT)
670 self.title.pack(side="top", fill="x", pady=30)
671
672 self.welcomeMsg = tk.Label(self, text = "Welcome to the Physics Learning Aid!")
673 self.label1 = tk.Label(self, text = "Are you a")
674 self.student = tk.Button(self, text = "Student", command=lambda: controller.show_frame(accountPage),height=2,width=20)
675 self.label2 = tk.Label(self, text = "or ")
676 self.teacher = tk.Button(self, text = "Teacher", command=lambda: controller.show_frame(adminLoginPage),height=2,width=20)
677
678 self.welcomeMsg.pack(side="top", fill="x", padx=5, pady=20)
679 self.label1.pack(side="top", fill="x", pady=5)
680 self.student.pack(side="top", pady=5)
681 self.label2.pack(side="top", fill="x", pady=5)
682 self.teacher.pack(side="top", pady=5)
683
684class accountPage(tk.Frame):
685
686 def __init__(self, parent, controller):
687
688 tk.Frame.__init__(self, parent)
689 self.title = tk.Label(self, text="Account Page", font=LARGEFONT)
690 self.title.pack(side="top", fill="x", pady=30)
691
692 self.login = tk.Button(self, text = "Existing User - Log in", command=lambda: controller.show_frame(loginPage),height=2,width=20)
693 self.signUp = tk.Button(self, text = "New User - Sign Up", command=lambda: controller.show_frame(signupPage),height=2,width=20)
694 self.logoutButton = tk.Button(self, text = "Log out", command=lambda: window.logout())
695 self.selectButton = tk.Button(self,text = "Score", command=lambda: showScore())
696 self.changePasswordButton = tk.Button(self, text = "Change Password", command=lambda: changePassword())
697
698 self.login.pack(side="top", fill="x", padx=300, pady=20)
699 self.signUp.pack(side="top", fill="x", padx=300, pady=5)
700 self.selectButton.pack(side="top", pady=20)
701 self.logoutButton.pack(side="bottom", pady=10)
702 self.changePasswordButton.pack(side="bottom")
703
704 # Displays the user's details, their username, current score and the date they last were on the program
705 def showScore():
706 if window.activeUser != "" and window.activeUser != "Admin":
707 score = USERNAMES[window.activeUser][1]
708 messagebox.showinfo("Your Details","Username : {0}\n\nScore : {1}".format(window.activeUser,score), icon="info")
709 else:
710 messagebox.showinfo("Error","Cannot display score\n\nYou are not logged in as a student", icon="warning")
711
712 def changePassword():
713 if window.activeUser != "" and window.activeUser != "Admin": # Don't switch to changePasswordPage if noone is logged off, or the admin is logged in
714 controller.show_frame(changePasswordPage)
715 else:
716 messagebox.showinfo("Error","Please login before trying to change your password", icon="warning")
717 self.login.focus()
718
719class changePasswordPage(tk.Frame):
720
721 def __init__(self, parent, controller):
722
723 tk.Frame.__init__(self, parent)
724 self.title = tk.Label(self, text="Change Password", font=LARGEFONT)
725 self.title.pack(padx=10, pady=10)
726
727 self.originalPasswordText = tk.Label(self, text = "Original Password:")
728 self.originalPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
729
730 self.newPasswordText = tk.Label(self, text = "New Password:")
731 self.newPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
732
733 self.confirmPasswordText = tk.Label(self, text = "Confirm Password:")
734 self.confirmPasswordInput = tk.Entry(self, justify="center", show="*", width=30)
735
736 self.changePasswordButton = tk.Button(self,text = "Change Password", command=lambda: changePassword())
737
738 self.originalPasswordText.pack(side="top", fill="x", padx=20, pady=5)
739 self.originalPasswordInput.pack(side="top")
740 self.originalPasswordInput.focus()
741
742 self.newPasswordText.pack(side="top", fill="x", padx=20, pady=5)
743 self.newPasswordInput.pack(side="top")
744
745 self.confirmPasswordText.pack(side="top", fill="x", padx=20, pady=5)
746 self.confirmPasswordInput.pack(side="top")
747
748 self.changePasswordButton.pack(side="top", fill="y", padx=20, pady=10)
749
750 def changePassword():
751
752 if self.originalPasswordInput.get() == USERNAMES[window.activeUser][0]:
753 if self.newPasswordInput.get() == self.confirmPasswordInput.get():
754 USERNAMES[window.activeUser][0] = self.newPasswordInput.get()
755 messagebox.showinfo("Password Changed","Your password has been changed successfully!", icon="info")
756 controller.show_frame(accountPage)
757 else:
758 messagebox.showinfo("Error","Your new and confirmation password do not match\nPlease try again", icon="warning")
759 self.newPasswordInput.delete(0,"end")
760 self.confirmPasswordInput.delete(0,"end")
761 self.newPasswordInput.focus()
762 else:
763 messagebox.showinfo("Error","Incorrect original password\nPlease try again", icon="warning")
764 self.originalPasswordInput.delete(0,"end")
765 self.newPasswordInput.delete(0,"end")
766 self.confirmPasswordInput.delete(0,"end")
767 self.newPasswordInput.focus()
768
769class loginPage(tk.Frame):
770
771 def __init__(self, parent, controller):
772
773 tk.Frame.__init__(self, parent)
774 self.title = tk.Label(self, text="Login", font=LARGEFONT)
775 self.title.pack(side="top", fill="x", pady=30)
776
777 # User details entry boxes
778 self.usernameText = tk.Label(self, text = "Username:")
779 self.usernameInput = tk.Entry(self, justify="center", width=30)
780 self.passwordText = tk.Label(self, text = "Password:")
781 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
782
783 self.loginButton = tk.Button(self,text = "Login", command=lambda: login())
784
785 self.usernameText.pack(side="top", fill="x", padx=20, pady=5)
786 self.usernameInput.pack(side="top", pady=5)
787 self.usernameInput.focus()
788 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
789 self.passwordInput.pack(side="top", pady=5)
790 self.loginButton.pack(side="top", pady="10")
791
792 def login(): # Validation ensuring username exists, and that the password given matches the saved password
793 if self.usernameInput.get() in USERNAMES: # Checks if the username exists in the database
794 if self.passwordInput.get() == USERNAMES[self.usernameInput.get()][0]: # Checks the password matches that of the stored password
795 window.activeUser = self.usernameInput.get()
796 messagebox.showinfo("Login Successful","Welcome back {0}".format(self.usernameInput.get()), icon = "info")
797 self.usernameInput.delete(0,"end") # Deletes value in entry box as it will not be removed automatically when a new frame is raised
798 self.passwordInput.delete(0,"end")
799 controller.show_frame(homePage)
800 window.enableWidgets()
801 else:
802 self.passwordInput.delete(0,"end")
803 messagebox.showinfo("Login Failed", "Incorrect Password", icon = "warning")
804 self.passwordInput.focus()
805 else:
806 self.usernameInput.delete(0,"end")
807 self.passwordInput.delete(0,"end")
808 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")
809 self.usernameInput.focus()
810
811class signupPage(tk.Frame):
812
813 def __init__(self, parent, controller):
814
815 tk.Frame.__init__(self, parent)
816 title = tk.Label(self, text="Sign Up", font=LARGEFONT)
817 title.pack(padx=10, pady=10)
818
819 self.firstNameText = tk.Label(self, text = "First Name:")
820 self.firstNameInput = tk.Entry(self, justify="center", width=30)
821
822 self.secondNameText = tk.Label(self, text = "Second Name:")
823 self.secondNameInput = tk.Entry(self, justify="center", width=30)
824
825 self.yearOfEntryText = tk.Label(self, text = "Year of Entry to SJP (YYYY):")
826 self.yearOfEntryInput = tk.Entry(self, justify="center", width=30)
827
828 self.passwordText = tk.Label(self, text = "New Password:")
829 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
830
831 self.passwordText2 = tk.Label(self, text = "Confirm Password:")
832 self.passwordInput2 = tk.Entry(self, show="*", justify="center", width=30)
833
834 self.signupButton = tk.Button(self,text = "Register", command=lambda: signup())
835
836 # Pack the elements onto the GUI window
837 self.firstNameText.pack(side="top", fill="x", padx=20, pady=5)
838 self.firstNameInput.pack(side="top")
839 self.firstNameInput.focus()
840
841 self.secondNameText.pack(side="top", fill="x", padx=20, pady=5)
842 self.secondNameInput.pack(side="top")
843
844 self.yearOfEntryText.pack(side="top", fill="x", padx=20, pady=5)
845 self.yearOfEntryInput.pack(side="top")
846
847 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
848 self.passwordInput.pack(side="top")
849
850 self.passwordText2.pack(side="top", fill="x", padx=20, pady=5)
851 self.passwordInput2.pack(side="top")
852
853 self.signupButton.pack(side="top", fill="y", padx=20, pady=10)
854
855 def signup():
856
857 # Create local variables that are easier to manipulate inside the function
858 firstName = self.firstNameInput.get()
859 secondName = self.secondNameInput.get()
860 yearOfEntry = self.yearOfEntryInput.get()
861 password = self.passwordInput.get()
862
863 if firstName == "" or secondName == "" or yearOfEntry == "" or password == "":
864 messagebox.showinfo("Error!","Please do not leave any fields blank", icon = "warning")
865 else:
866 if yearOfEntry.isdigit() == False or len(yearOfEntry) != 4:
867 messagebox.showinfo("Invalid Year of Entry", "Please enter a valid 4 digit year", icon = "warning")
868 self.yearOfEntryInput.delete(0,"end")
869 self.yearOfEntryInput.focus()
870
871 else:
872 if self.passwordInput.get() != self.passwordInput2.get():
873 messagebox.showinfo("Passwords do not match", "The passwords you entered did not match\n\nPlease ensure they match", icon = "warning")
874 self.passwordInput.delete(0,"end")
875 self.passwordInput2.delete(0,"end")
876 self.passwordInput.focus()
877
878 else:
879 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
880 if newUsername in USERNAMES:
881 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")
882 self.firstNameInput.delete(0,"end")
883 self.secondNameInput.delete(0,"end")
884 self.yearOfEntryInput.delete(0,"end")
885 self.passwordInput.delete(0,"end")
886 self.passwordInput2.delete(0,"end")
887 self.firstNameInput.focus() # Resets the form and focuses the keyboard onto the first entry field
888 else:
889 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
890 lastSession = [] # This will hold the date which the student last logged out on, it is overwritten when the user logs out of the program
891 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
892
893 window.activeUser = newUsername # Assigns the active user as the newly registered user
894
895 messagebox.showinfo("Registered","Welcome {0}".format(newUsername), icon = "info")
896
897 self.firstNameInput.delete(0,"end")
898 self.secondNameInput.delete(0,"end")
899 self.yearOfEntryInput.delete(0,"end")
900 self.passwordInput.delete(0,"end")
901 self.passwordInput2.delete(0,"end")
902 window.enableWidgets()
903 controller.show_frame(homePage)
904
905class adminLoginPage(tk.Frame):
906
907 def __init__(self, parent, controller):
908
909 tk.Frame.__init__(self, parent)
910 self.title = tk.Label(self, text="Admin Login Page", font=LARGEFONT)
911 self.title.pack(side="top", fill="x", pady=30)
912
913 self.usernameText = tk.Label(self, text = "Admin Username:")
914 self.usernameInput = tk.Entry(self, justify="center", width=30)
915 self.passwordText = tk.Label(self, text = "Admin Password:")
916 self.passwordInput = tk.Entry(self, show="*", justify="center", width=30)
917
918 self.loginButton = tk.Button(self,text = "Login", command=lambda: adminLogin())
919
920 self.usernameText.pack(side="top", fill="x", padx=20, pady=5)
921 self.usernameInput.pack(side="top", pady=5)
922 self.usernameInput.focus()
923 self.passwordText.pack(side="top", fill="x", padx=20, pady=5)
924 self.passwordInput.pack(side="top", pady=5)
925 self.loginButton.pack(side="top", pady="10")
926
927 def adminLogin():
928 if self.usernameInput.get() == ADMIN[0]:
929 if pbkdf2_sha256.verify(self.passwordInput.get(), ADMIN[1]):
930 window.activeUser = self.usernameInput.get()
931 messagebox.showinfo("Login Successful","Continue to Admin Page".format(self.usernameInput.get()), icon = "info")
932 self.usernameInput.delete(0,"end")
933 self.passwordInput.delete(0,"end")
934 controller.show_frame(adminPage)
935 window.enableWidgets()
936 else:
937 self.usernameInput.delete(0,"end")
938 self.passwordInput.delete(0,"end")
939 messagebox.showinfo("Login Failed", "Incorrect details", icon = "warning")
940 self.usernameInput.focus()
941 else:
942 self.usernameInput.delete(0,"end")
943 self.passwordInput.delete(0,"end")
944 messagebox.showinfo("Login Failed", "Incorrect details", icon = "warning")
945 self.usernameInput.focus()
946
947class adminPage(tk.Frame):
948
949 def __init__(self, parent, controller):
950
951 tk.Frame.__init__(self, parent)
952 self.title = tk.Label(self, text="Admin Page", font=LARGEFONT)
953 self.title.pack(padx=10, pady=10)
954
955 username = StringVar()
956 username.set("Usernames")
957
958 self.usernameText = tk.Label(self, text = "Select students to review details:")
959 self.usernamesList = list(USERNAMES) # Creates an array, holding all the key values from the dictionary USERNAMES
960 if self.usernamesList == []:
961 self.usernamesList.append("No active users")
962 self.dropdown = OptionMenu(self, username, *self.usernamesList)
963
964 self.updateButton = tk.Button(self,text = "Update and Sort (Alphabetically)", command=lambda: updateUsernames())
965 self.selectButton = tk.Button(self,text = "Search", command=lambda: searchUsername())
966
967 self.usernameText.pack(side="top", padx=20, pady=5)
968 self.dropdown.pack(side="top", padx=20, pady=5)
969 self.updateButton.pack(side="top", pady=10)
970 self.selectButton.pack(side="top")
971
972 # Displays the user's details, their username, current score and the date they last were on the program
973 def searchUsername():
974 if username.get() in USERNAMES:
975 score = USERNAMES[username.get()][1]
976 lastSession = USERNAMES[username.get()][2]
977 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]))
978 username.set("Usernames")
979
980 # Updates the usernames list, sorting them into alphabetical order of their username, allowing the teacher to easily search for a student
981 def updateUsernames():
982 username.set("Usernames")
983 self.dropdown["menu"].delete(0,"end")
984
985 self.usernamesList = list(USERNAMES) # Takes into account any new users
986 self.usernamesList = mergeSort(self.usernamesList) # Calls global function to merge sort the list of
987 for each in self.usernamesList:
988 self.dropdown["menu"].add_command(label=each, command=tk._setit(username, each))
989
990class homePage(tk.Frame):
991
992 def __init__(self, parent, controller):
993
994 tk.Frame.__init__(self, parent)
995 self.title = tk.Label(self, text="Learning Options Page", font=LARGEFONT)
996 self.title.pack(padx=10, pady=10)
997
998 self.welcomeMsg = tk.Label(self, text = "Welcome to the Physics Learning Aid!")
999 self.forcesButton = tk.Button(self, text = "1. Forces", command=lambda: controller.show_frame(forcesPage),height=2,width=20)
1000 self.label1 = tk.Label(self, text = "or ")
1001 self.timeGraphsButton = tk.Button(self, text = "2. Time Graphs", command=lambda: controller.show_frame(timeGraphsPage),height=2,width=20)
1002 self.label2 = tk.Label(self, text = "or ")
1003 self.SUVATRevisionButton = tk.Button(self, text = "3. SUVAT Revision", command=lambda: controller.show_frame(SUVATRevisionPage),height=2,width=20)
1004
1005 self.welcomeMsg.pack(side="top", fill="x", padx=5, pady=20)
1006 self.forcesButton.pack(side="top", pady=5)
1007 self.label1.pack(side="top", fill="x", pady=5)
1008 self.timeGraphsButton.pack(side="top", pady=5)
1009 self.label2.pack(side="top", fill="x", pady=5)
1010 self.SUVATRevisionButton.pack(side="top", pady=5)
1011
1012# Forces Pages #
1013
1014class forcesPage(tk.Frame):
1015
1016 def __init__(self, parent, controller):
1017
1018 tk.Frame.__init__(self, parent)
1019 self.title = tk.Label(self, text="Forces Page", font=LARGEFONT)
1020 self.title.pack(padx=10, pady=10)
1021
1022 self.label = tk.Label(self, text = "Forces Topics:", font = MEDIUMFONT)
1023 self.ScalarsVectorsButton = tk.Button(self, text = "Scalars and Vectors", command=lambda: controller.show_frame(ScalarsVectorsPage))
1024 self.AdditionVectorsCalculationButton = tk.Button(self, text = "Addition of Vectors - Calculation", command=lambda: controller.show_frame(AdditionVectorsCalculationPage))
1025 self.AdditionVectorsScaleDrawingButton = tk.Button(self, text = "Addition of Vectors - Scale Drawing", command=lambda: controller.show_frame(AdditionVectorsScaleDrawingPage))
1026 self.ResolutionOfVectorsButton = tk.Button(self, text = "Resolution of Vectors", command=lambda: controller.show_frame(ResolutionOfVectorsPage))
1027 self.VectorsInEquilibriumButton = tk.Button(self, text = "Vectors in Equilibrium", command=lambda: controller.show_frame(VectorsInEquilibriumPage))
1028 self.InclinedPlaneVectorsButton = tk.Button(self, text = "Inclined Plane Vectors", command=lambda: controller.show_frame(InclinedPlaneVectorsPage))
1029
1030 self.label.pack(side="top", fill="x", pady=10)
1031 self.ScalarsVectorsButton.pack(side="top", pady=15, ipadx=10)
1032 self.AdditionVectorsCalculationButton.pack(side="top", pady=15, ipadx=10)
1033 self.AdditionVectorsScaleDrawingButton.pack(side="top", pady=15, ipadx=10)
1034 self.ResolutionOfVectorsButton.pack(side="top", pady=15, ipadx=10)
1035 self.VectorsInEquilibriumButton.pack(side="top", pady=15, ipadx=10)
1036 self.InclinedPlaneVectorsButton.pack(side="top", pady=15, ipadx=10)
1037
1038class ScalarsVectorsPage(tk.Frame):
1039
1040 def __init__(self, parent, controller):
1041
1042 tk.Frame.__init__(self, parent)
1043 self.title = tk.Label(self, text="Scalars and Vectors Page", font=LARGEFONT)
1044 self.title.pack(padx=10, pady=10)
1045
1046 self.canvas = Canvas(self, width=800, height=500, bd=2)
1047 self.canvas.pack()
1048
1049 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1050 self.returnButton.pack(ipadx=10)
1051
1052 self.canvas.create_line(400, 0, 400, 350)
1053 self.canvas.create_line(400, 400, 400, 500)
1054 self.canvas.create_line(20, 375, 325, 375)
1055 self.canvas.create_line(475, 375, 780, 375)
1056
1057 self.canvas.create_text(200, 15, text = "Scalars", font = MEDIUMFONT)
1058 self.canvas.create_line(100, 175, 300, 75, arrow=BOTH, arrowshape="2 1 10")
1059 self.canvas.create_text(160, 100, text = "2 metres", font = LABELFONT)
1060 self.canvas.create_text(200, 250, text = "Scalars have size / length / magnitude", font = LABELFONT)
1061 self.canvas.create_text(200, 300, text = "They have no direction", font = MEDIUMFONT)
1062
1063 self.canvas.create_text(600, 15, text = "Vectors", font = MEDIUMFONT)
1064 self.canvas.create_text(500, 60, text = "North", font = LABELFONT)
1065 self.canvas.create_line(500, 175, 500, 75, arrow=LAST) # North arrow
1066 self.canvas.create_line(500, 175, 700, 75, arrow=LAST) # Scalar arrow
1067 self.canvas.create_arc(475, 150, 525, 200, start = 90.0, extent = -63.44)
1068 self.canvas.create_text(660, 135, text = "2 metres", font = LABELFONT)
1069 self.canvas.create_text(520, 140, text = "Θ", font = LABELFONT)
1070 self.canvas.create_text(600, 250, text = "Vectors have size / length / magnitude", font = LABELFONT)
1071 self.canvas.create_text(600, 300, text = "They also have a direction / bearing", font = LABELFONT)
1072
1073 self.canvas.create_text(400, 375, text = "Examples", font = MEDIUMFONT)
1074
1075 self.canvas.create_text(120, 435, text = "Speed", font = MEDIUMFONT)
1076 self.canvas.create_text(200, 470, text = "Mass", font = MEDIUMFONT)
1077 self.canvas.create_text(280, 435, text = "Distance", font = MEDIUMFONT)
1078
1079 self.canvas.create_text(465, 435, text = "Velocity", font = MEDIUMFONT)
1080 self.canvas.create_text(515, 470, text = "Force", font = MEDIUMFONT)
1081 self.canvas.create_text(565, 435, text = "Weight", font = MEDIUMFONT)
1082 self.canvas.create_text(650, 470, text = "Acceleration", font = MEDIUMFONT)
1083 self.canvas.create_text(700, 435, text = "Displacement", font = MEDIUMFONT)
1084
1085 def returnForces():
1086 window.addScore(2)
1087 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1088 controller.show_frame(forcesPage)
1089
1090class AdditionVectorsCalculationPage(tk.Frame):
1091
1092 def __init__(self, parent, controller):
1093
1094 tk.Frame.__init__(self, parent)
1095 self.title = tk.Label(self, text="Addition of Vectors - Calculation Page", font=LARGEFONT)
1096 self.title.pack(padx=10, pady=10)
1097
1098 self.canvas = Canvas(self, width=800, height=500, bd=2)
1099 self.canvas.pack()
1100
1101 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1102 self.returnButton.pack(ipadx=10)
1103
1104 self.canvas.create_line(25, 150, 425, 20, arrow=LAST, arrowshape="16 20 6")
1105 self.canvas.create_rectangle(400, 125, 425, 150)
1106 self.canvas.create_arc(-50, 100, 100, 200, extent = 25.5)
1107 self.canvas.create_text(110, 135, text = "Θ", font = LABELFONT)
1108
1109 self.canvas.create_line(425, 150, 425,50, arrow=LAST, arrowshape="12 16 4", fill="red")
1110 self.canvas.create_line(425, 50, 425, 20, fill="red")
1111 self.canvas.create_line(25, 150, 375, 150, arrow=LAST, arrowshape="12 16 4", fill="blue")
1112 self.canvas.create_line(375, 150, 425, 150, fill="blue")
1113 self.canvas.create_text(450, 75, text = "a", font = LABELFONT, fill="red")
1114 self.canvas.create_text(225, 170, text = "b", font = LABELFONT, fill="blue")
1115 self.canvas.create_text(225, 50, text = "c", font = LABELFONT)
1116
1117 self.canvas.create_text(625, 75, text = """You will only ever be asked
1118to add vectors by calculation
1119with vectors at right angles
1120
1121Therefore, addition of vectors
1122by calculation simply requires
1123Pythagoras's Theorem""", font = LABELFONT)
1124
1125 self.canvas.create_text(175, 225, text = "PYTHAGORAS", font = LARGEFONT)
1126 self.canvas.create_text(175, 275, text = "a^2 + b^2 = c^2", font = LARGEFONT)
1127 self.canvas.create_text(175, 375, text = "TRIGONOMETRY", font = LARGEFONT)
1128 self.canvas.create_text(175, 425, text = "tan Θ = a / b", font = LARGEFONT)
1129 self.canvas.create_text(550, 325, text = """You may be asked to find the final vector
1130after travelling along two vectors
1131
1132In which case, substitute in 'a' and 'b'
1133and rearrange to find 'c'
1134which will be your vector's magnitude
1135Use trigonometry to find
1136the angle of the vector""", font = LABELFONT)
1137
1138 def returnForces():
1139 window.addScore(2)
1140 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1141 controller.show_frame(forcesPage)
1142
1143class AdditionVectorsScaleDrawingPage(tk.Frame):
1144
1145 def __init__(self, parent, controller):
1146
1147 tk.Frame.__init__(self, parent)
1148 self.title = tk.Label(self, text="Addition of Vectors - Scale Drawing Page", font=LARGEFONT)
1149 self.title.pack(padx=10, pady=10)
1150
1151 self.canvas = Canvas(self, width=800, height=500, bd=2)
1152 self.canvas.pack()
1153
1154 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1155 self.returnButton.pack(ipadx=10)
1156
1157 self.canvas.create_text(50, 10, text = "North", font = LABELFONT)
1158 self.canvas.create_line(50, 200, 50, 20, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1159 self.canvas.create_arc(25, 175, 75, 225, start = 90.0, extent = -45.0)
1160
1161 self.canvas.create_text(175, 10, text = "North", font = LABELFONT)
1162 self.canvas.create_line(175, 75, 175, 20, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1163 self.canvas.create_rectangle(175, 75, 190, 60)
1164
1165 self.canvas.create_line(50, 200, 175, 75, arrow=LAST, arrowshape="16 20 6", fill="red")
1166 self.canvas.create_line(175, 75, 425, 75, arrow=LAST, arrowshape="16 20 6", fill="blue")
1167 self.canvas.create_text(160, 155, text = "10 KM\nVector a", font = LABELFONT, fill="red")
1168 self.canvas.create_text(300, 100, text = "20 KM\nVector b", font = LABELFONT, fill="blue")
1169 self.canvas.create_text(75, 145, text = "45°", font = LABELFONT)
1170 self.canvas.create_text(620, 125, text = """You can be asked to add two vectors
1171by scale drawing
1172
1173The angle between these vectors is
1174NOT limited to right angles
1175
1176You must draw the vectors
1177to scale""", font = LABELFONT)
1178
1179 self.canvas.create_text(400, 240, text = "DRAW TO SCALE - DECLARE YOUR SCALE", font = LARGEFONT)
1180 self.canvas.create_rectangle(15, 285, 145, 315)
1181 self.canvas.create_text(80, 300, text = "1 KM = 1 CM", font = LABELFONT)
1182
1183 self.canvas.create_text(50, 340, text = "North", font = LABELFONT)
1184 self.canvas.create_line(50, 460, 50, 350, dash=True, arrow=LAST, arrowshape="12 16 4") # North Arrow
1185 self.canvas.create_arc(25, 435, 75, 485, start = 90.0, extent = -72.5)
1186 self.canvas.create_text(65, 425, text = "Θ", font = LABELFONT)
1187
1188 self.canvas.create_line(50, 460, 175, 335, arrow=LAST, arrowshape="16 20 6", fill="red")
1189 self.canvas.create_line(175, 335, 425, 335, arrow=LAST, arrowshape="16 20 6", fill="blue")
1190 self.canvas.create_text(120, 370, text = "a", font = LABELFONT, fill="red")
1191 self.canvas.create_text(280, 320, text = "b", font = LABELFONT, fill="blue")
1192 self.canvas.create_text(150, 400, text = "10 CM", font = LABELFONT, fill="red")
1193 self.canvas.create_text(280, 355, text = "20 CM", font = LABELFONT, fill="blue")
1194 self.canvas.create_line(50, 460, 425, 335, fill="red")
1195 self.canvas.create_line(50, 460, 425, 335, dash=True, fill="blue")
1196 self.canvas.create_text(300, 420, text = "Resultant Vector", font = MEDIUMFONT, fill="red")
1197
1198 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)
1199
1200 def returnForces():
1201 window.addScore(2)
1202 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1203 controller.show_frame(forcesPage)
1204
1205class ResolutionOfVectorsPage(tk.Frame):
1206
1207 def __init__(self, parent, controller):
1208
1209 tk.Frame.__init__(self, parent)
1210 self.title = tk.Label(self, text="Resolution of Vectors Page", font=LARGEFONT)
1211 self.title.pack(padx=10, pady=10)
1212
1213 self.canvas = Canvas(self, width=800, height=500, bd=2)
1214 self.canvas.pack()
1215
1216 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1217 self.returnButton.pack(ipadx=10)
1218
1219 self.canvas.create_line(50, 150, 450, 20, arrow=LAST, arrowshape="16 20 6")
1220 self.canvas.create_line(50, 150, 50, 20, dash=True, arrow=LAST, arrowshape="12 16 4", fill="red")
1221 self.canvas.create_line(50, 150, 450, 150, dash=True, arrow=LAST, arrowshape="12 16 4", fill="blue")
1222 self.canvas.create_text(250, 50, text = "? ms^-1", font = LABELFONT)
1223 self.canvas.create_text(100, 75, text = "3 ms^-1", font = LABELFONT, fill="red")
1224 self.canvas.create_text(250, 175, text = "4 ms^-1", font = LABELFONT, fill="blue")
1225 self.canvas.create_text(625, 100, text = """Similar to addition of vectors
1226by calculation
1227
1228Every vector has a vertical
1229and horizontal component
1230
1231Simply add the two components
1232in order to find the vector's
1233magnitude""", font = LABELFONT)
1234
1235 self.canvas.create_line(350, 390, 750, 240, arrow=LAST, arrowshape="16 20 6")
1236 self.canvas.create_rectangle(725, 365, 750, 390)
1237 self.canvas.create_arc(300, 340, 400, 440, extent = 20.5)
1238 self.canvas.create_text(420, 377, text = "Θ", font = LABELFONT)
1239
1240 self.canvas.create_line(750, 390, 750, 290, arrow=LAST, arrowshape="12 16 4", fill="red")
1241 self.canvas.create_line(750, 290, 750, 240, fill="red")
1242 self.canvas.create_line(350, 390, 700, 390, arrow=LAST, arrowshape="12 16 4", fill="blue")
1243 self.canvas.create_line(700, 390, 750, 390, fill="blue")
1244 self.canvas.create_text(775, 315, text = "a", font = LABELFONT, fill="red")
1245 self.canvas.create_text(550, 410, text = "b", font = LABELFONT, fill="blue")
1246 self.canvas.create_text(550, 290, text = "c", font = LABELFONT)
1247
1248 self.canvas.create_text(100, 245, text = "a^2 + b^2 = c^2", font = LABELFONT)
1249 self.canvas.create_text(100, 295, text = "3^2 + 4^2 = c^2", font = LABELFONT)
1250 self.canvas.create_text(100, 345, text = "c^2 = 25", font = LABELFONT)
1251 self.canvas.create_text(100, 395, text = "c = 5", font = LABELFONT)
1252 self.canvas.create_text(310, 245, text = "arg Θ = 3 / 4", font = LABELFONT)
1253 self.canvas.create_text(310, 295, text = "Θ = tan^-1( 0.75 )", font = LABELFONT)
1254 self.canvas.create_text(310, 345, text = "Θ ≈ 53°", font = LABELFONT)
1255
1256 self.canvas.create_text(475, 465, text = "Vector Magnitude is 5 ms^-1, Vector Angle is 53°", font = LABELFONT)
1257
1258 def returnForces():
1259 window.addScore(2)
1260 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1261 controller.show_frame(forcesPage)
1262
1263class VectorsInEquilibriumPage(tk.Frame):
1264
1265 def __init__(self, parent, controller):
1266
1267 tk.Frame.__init__(self, parent)
1268 self.title = tk.Label(self, text="Vectors In Equilibrium Page", font=LARGEFONT)
1269 self.title.pack(padx=10, pady=10)
1270
1271 self.canvas = Canvas(self, width=800, height=500, bd=2)
1272 self.canvas.pack()
1273
1274 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1275 self.returnButton.pack(ipadx=10)
1276
1277 self.canvas.create_text(400, 20, text = "F = ma", font = LARGEFONT)
1278 self.canvas.create_text(400, 50, text = "When an object is in equilibrium, the resultant force acting on it is 0", font = LABELFONT)
1279 self.canvas.create_text(400, 70, text = "If F = 0, ma = 0. Assuming the object has mass, when in equilibrium, acceleration = 0", font = LABELFONT)
1280 self.canvas.create_text(400, 90, text = "When acceleration = 0, the object has constant velocity", font = LABELFONT)
1281 # Underline
1282 self.canvas.create_line(160, 102, 640, 102)
1283
1284 # Page divider
1285 self.canvas.create_line(400, 110, 400, 490)
1286
1287 # At rest
1288 self.canvas.create_text(200, 135, text = "Objects in equilibrium - at rest", font = MEDIUMFONT)
1289 self.canvas.create_line(20, 260, 380, 260)
1290 self.canvas.create_rectangle(175, 210, 225, 260)
1291 self.canvas.create_oval(198, 233, 202, 237, fill="black")
1292 self.canvas.create_text(50, 250, text = "Table", font = SMALLFONT)
1293
1294 self.canvas.create_line(200, 160, 200, 235, fill="blue", arrow=FIRST)
1295 self.canvas.create_text(280, 185, text = "Reaction Force", font = LABELFONT, fill="blue")
1296 self.canvas.create_line(200, 235, 200, 310, fill="red", arrow=LAST)
1297 self.canvas.create_text(165, 285, text = "Weight ", font = LABELFONT, fill="red")
1298
1299 self.canvas.create_text(200, 350, text = "The table supports the block by exerting a", font = LABELFONT)
1300 self.canvas.create_text(200, 390, text = "reaction force equal to the blocks weight", font = LABELFONT)
1301 self.canvas.create_text(200, 430, text = "Resultant force is 0 and so its acceleration", font = LABELFONT)
1302 self.canvas.create_text(200, 470, text = "is 0. Therefore it stays at rest", font = LABELFONT)
1303
1304 # Moving
1305 self.canvas.create_text(600, 135, text = "Objects in equilibrium - moving", font = MEDIUMFONT)
1306 self.canvas.create_text(600, 180, text = "Velocity = 30 ms^-1", font = LABELFONT)
1307 self.canvas.create_line(420, 290, 780, 290)
1308 try:
1309 self.Car = tk.PhotoImage(file = "NEACar.png")
1310 self.canvas.create_image(600, 260, image=self.Car)
1311 except:
1312 self.canvas.create_text(600, 260, text = "Car Image not found")
1313 self.canvas.create_line(537, 260, 450, 260, fill="red", arrow=LAST)
1314 self.canvas.create_text(490, 240, text = "Thrust ", font = LABELFONT, fill="red")
1315 self.canvas.create_line(663, 260, 750, 260, fill="blue", arrow=LAST)
1316 self.canvas.create_text(710, 230, text = "Air Resistance", font = LABELFONT, fill="blue")
1317 self.canvas.create_line(635, 290, 750, 290, fill="blue", arrow=LAST)
1318 self.canvas.create_text(690, 305, text = "Friction", font = LABELFONT, fill="blue")
1319
1320 self.canvas.create_text(600, 345, text = "If in equilibrium,", font = LABELFONT)
1321 self.canvas.create_text(600, 375, text = "Thrust = Air Resistance + Friction,", font = LABELFONT)
1322 self.canvas.create_text(600, 405, text = "so the overall force on the car is 0", font = LABELFONT)
1323 self.canvas.create_text(600, 435, text = "So its acceleration is 0", font = LABELFONT)
1324 self.canvas.create_text(600, 465, text = "Therefore it stays at its current velocity", font = LABELFONT)
1325
1326
1327 def returnForces():
1328 window.addScore(2)
1329 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1330 controller.show_frame(forcesPage)
1331
1332class InclinedPlaneVectorsPage(tk.Frame):
1333
1334 def __init__(self, parent, controller):
1335
1336 tk.Frame.__init__(self, parent)
1337 self.title = tk.Label(self, text="Inclined Plane Vectors Page", font=LARGEFONT)
1338 self.title.pack(padx=10, pady=10)
1339
1340 self.canvas = Canvas(self, width=800, height=500, bd=2)
1341 self.canvas.pack()
1342
1343 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnForces())
1344 self.returnButton.pack(ipadx=10)
1345
1346 self.canvas.create_text(400, 25, text = "When an object is stationary on an inclined plane, 3 forces are acting on it", font = MEDIUMFONT)
1347 self.canvas.create_text(400, 50, text = "1. Weight of the object, acting vertically downwards", font = SMALLFONT)
1348 self.canvas.create_text(400, 75, text = "2. The Support / Reaction force, acting perpendicular to where the weight acts on the plane", font = SMALLFONT)
1349 self.canvas.create_text(400, 100, text = "3. Friction, acting parallel to the plane", font = SMALLFONT)
1350
1351 # Box
1352 self.canvas.create_polygon(120, 225, 160, 215, 150, 175, 110, 185, fill="white", outline="black")
1353 self.canvas.create_oval(133, 198, 137, 202, fill="black")
1354
1355 # Weight arrow
1356 self.canvas.create_line(135, 125, 135, 220, fill="green", arrow=LAST)
1357 self.canvas.create_text(160, 150, text = "Weight", fill="green")
1358
1359 # Support Arrow
1360 self.canvas.create_line(135, 220, 90, 135, fill="blue", arrow=LAST)
1361 self.canvas.create_text(70, 160, text = "Support", fill="blue")
1362
1363 # Friction Arrows
1364 self.canvas.create_line(90, 135, 135, 125, fill="red", arrow=LAST)
1365 self.canvas.create_text(105, 115, text = "Friction", fill="red")
1366 self.canvas.create_line(135, 220, 175, 210, fill="red", arrow=LAST)
1367
1368 # Triangle
1369 self.canvas.create_arc(-40, 190, 80, 310, extent = 14.0)
1370 self.canvas.create_line(20, 250, 500, 250, fill="green")
1371 self.canvas.create_line(500, 250, 420, 150, fill="red")
1372 self.canvas.create_line(20, 250, 420, 150, fill="blue")
1373 self.canvas.create_text(100, 240, text = "Θ", font = SMALLFONT)
1374
1375 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)
1376
1377 # New Triangle
1378 self.canvas.create_arc(210, 390, 330, 510, extent = 14.0)
1379 self.canvas.create_line(270, 450, 750, 450, fill="green", arrow=FIRST)
1380 self.canvas.create_line(750, 450, 670, 350, fill="red", arrow=FIRST)
1381 self.canvas.create_line(270, 450, 670, 350, fill="blue", arrow=LAST)
1382 self.canvas.create_text(350, 440, text = "Θ", font = SMALLFONT)
1383 self.canvas.create_text(550, 465, text = "Weight", fill="green")
1384 self.canvas.create_text(470, 380, text = "Support", fill="blue")
1385 self.canvas.create_text(740, 390, text = "Friction", fill="red")
1386
1387 self.canvas.create_text(320, 350, text = """If asked to find out the angle of the inclined plane, use trigonometry
1388and your values of weight, support and friction to find Θ
1389
1390As the triangle's overall displacement is zero,
1391an unknown weight, support or friction force
1392will be equal to the negative sum of the
1393known values
1394e.g. W = -(S + F)""", font = LABELFONT)
1395 self.canvas.create_text(125, 475, text = "W + S + F = 0", font = MEDIUMFONT)
1396
1397 def returnForces():
1398 window.addScore(2)
1399 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1400 controller.show_frame(forcesPage)
1401
1402# Time Graph Pages #
1403
1404class timeGraphsPage(tk.Frame):
1405
1406 def __init__(self, parent, controller):
1407
1408 tk.Frame.__init__(self, parent)
1409 self.title = tk.Label(self, text="Time Graphs Page", font=LARGEFONT)
1410 self.title.pack(padx=10, pady=10)
1411
1412 self.label = tk.Label(self, text = "Time Graphs:", font = MEDIUMFONT)
1413 self.GradientsButton = tk.Button(self, text = "Gradients", command=lambda: controller.show_frame(GradientsPage))
1414 self.AreaUnderCurveButton = tk.Button(self, text = "Area Under Curve", command=lambda: controller.show_frame(AreaUnderCurvePage))
1415 self.BouncingBallButton = tk.Button(self, text = "Experiment - Bouncing Ball", command=lambda: controller.show_frame(BouncingBallPage))
1416 self.interactiveGraphsButton = tk.Button(self, text = "Interactive Graphs", command=lambda: controller.show_frame(interactiveGraphsPage))
1417
1418 self.label.pack(side="top", fill="x", pady=10)
1419 self.GradientsButton.pack(side="top", pady=15, ipadx=10)
1420 self.AreaUnderCurveButton.pack(side="top", pady=15, ipadx=10)
1421 self.BouncingBallButton.pack(side="top", pady=15, ipadx=10)
1422 self.interactiveGraphsButton.pack(side="top", pady=15, ipadx=10)
1423
1424class GradientsPage(tk.Frame):
1425
1426 def __init__(self, parent, controller):
1427
1428 tk.Frame.__init__(self, parent)
1429 self.title = tk.Label(self, text="Gradients Of Time Graphs Page", font=LARGEFONT)
1430 self.title.grid(row=0 , column=0, columnspan=2, padx=10, pady=10)
1431
1432 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1433 self.distanceCanvas.grid(row=1, column=0)
1434 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1435 self.displacementCanvas.grid(row=1, column=1)
1436 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1437 self.speedCanvas.grid(row=2, column=0)
1438 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1439 self.velocityCanvas.grid(row=2, column=1)
1440 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1441 self.accelerationCanvas.grid(row=3, column=0, columnspan=2)
1442
1443 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1444 self.returnButton.grid(row=4, column=0, ipadx=10)
1445
1446 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1447 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1448
1449 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph",font = MEDIUMFONT, fill="blue")
1450 self.distanceCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1451 self.distanceCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1452 self.distanceCanvas.create_line(50, 135, 195, 40, fill="blue")
1453 self.distanceCanvas.create_text(25, 85, text = "d",font = LABELFONT, fill="blue")
1454 self.distanceCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1455 self.distanceCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="blue")
1456 self.distanceCanvas.create_text(300, 85, text = "= d / t",font = LABELFONT, fill="blue")
1457 self.distanceCanvas.create_text(300, 115, text = "= Speed",font = LABELFONT, fill="blue")
1458
1459 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1460 self.displacementCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1461 self.displacementCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1462 self.displacementCanvas.create_line(50, 135, 195, 40, fill="red")
1463 self.displacementCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="red")
1464 self.displacementCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1465 self.displacementCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1466 self.displacementCanvas.create_text(300, 85, text = "= s / t",font = LABELFONT, fill="red")
1467 self.displacementCanvas.create_text(300, 115, text = "= Velocity",font = LABELFONT, fill="red")
1468
1469 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1470 self.speedCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1471 self.speedCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1472 self.speedCanvas.create_line(50, 135, 195, 40, fill="blue")
1473 self.speedCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="blue")
1474 self.speedCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1475 self.speedCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="blue")
1476 self.speedCanvas.create_text(300, 85, text = "= s / t",font = LABELFONT, fill="blue")
1477 self.speedCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="blue")
1478
1479 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1480 self.velocityCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1481 self.velocityCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1482 self.velocityCanvas.create_line(50, 135, 195, 40, fill="red")
1483 self.velocityCanvas.create_text(25, 85, text = "v",font = LABELFONT, fill="red")
1484 self.velocityCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1485 self.velocityCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1486 self.velocityCanvas.create_text(300, 85, text = "= v / t",font = LABELFONT, fill="red")
1487 self.velocityCanvas.create_text(300, 115, text = "Acceleration",font = LABELFONT, fill="red")
1488
1489 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1490 self.accelerationCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1491 self.accelerationCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1492 self.accelerationCanvas.create_line(50, 135, 195, 40, fill="red")
1493 self.accelerationCanvas.create_text(25, 85, text = "a",font = LABELFONT, fill="red")
1494 self.accelerationCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1495 self.accelerationCanvas.create_text(300, 55, text = "Gradient",font = LABELFONT, fill="red")
1496 self.accelerationCanvas.create_text(300, 85, text = "= a / t",font = LABELFONT, fill="red")
1497 self.accelerationCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="red")
1498
1499 def returnTimeGraphs():
1500 window.addScore(2)
1501 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1502 controller.show_frame(timeGraphsPage)
1503
1504class AreaUnderCurvePage(tk.Frame):
1505
1506 def __init__(self, parent, controller):
1507
1508 tk.Frame.__init__(self, parent)
1509 self.title = tk.Label(self, text="Area Under Curves Of Time Graphs Page", font=LARGEFONT)
1510 self.title.grid(row=0, column=0, columnspan=2, padx=10, pady=10)
1511
1512 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1513 self.distanceCanvas.grid(row=1, column=0)
1514 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1515 self.displacementCanvas.grid(row=1, column=1)
1516 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1517 self.speedCanvas.grid(row=2, column=0)
1518 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1519 self.velocityCanvas.grid(row=2, column=1)
1520 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1521 self.accelerationCanvas.grid(row=3, column=0, columnspan=2)
1522
1523 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1524 self.returnButton.grid(row=4, column=0, ipadx=10)
1525
1526 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1527 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1528
1529 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph",font = MEDIUMFONT, fill="blue")
1530 self.distanceCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1531 self.distanceCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1532 self.distanceCanvas.create_line(50, 135, 195, 40, fill="blue")
1533 self.distanceCanvas.create_text(25, 85, text = "d",font = LABELFONT, fill="blue")
1534 self.distanceCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1535 self.distanceCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="blue")
1536 self.distanceCanvas.create_text(300, 85, text = "= d x t",font = LABELFONT, fill="blue")
1537 self.distanceCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="blue")
1538
1539 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1540 self.displacementCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1541 self.displacementCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1542 self.displacementCanvas.create_line(50, 135, 195, 40, fill="red")
1543 self.displacementCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="red")
1544 self.displacementCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1545 self.displacementCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1546 self.displacementCanvas.create_text(300, 85, text = "= s x t",font = LABELFONT, fill="red")
1547 self.displacementCanvas.create_text(300, 115, text = "Not needed for exam",font = LABELFONT, fill="red")
1548
1549 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1550 self.speedCanvas.create_line(50, 135, 50, 35, width = 3, fill="blue")
1551 self.speedCanvas.create_line(50, 135, 200, 135, width = 3, fill="blue")
1552 self.speedCanvas.create_line(50, 135, 195, 40, fill="blue")
1553 self.speedCanvas.create_text(25, 85, text = "s",font = LABELFONT, fill="blue")
1554 self.speedCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="blue")
1555 self.speedCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="blue")
1556 self.speedCanvas.create_text(300, 85, text = "= s x t",font = LABELFONT, fill="blue")
1557 self.speedCanvas.create_text(300, 115, text = "= Distance",font = LABELFONT, fill="blue")
1558
1559 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1560 self.velocityCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1561 self.velocityCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1562 self.velocityCanvas.create_line(50, 135, 195, 40, fill="red")
1563 self.velocityCanvas.create_text(25, 85, text = "v",font = LABELFONT, fill="red")
1564 self.velocityCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1565 self.velocityCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1566 self.velocityCanvas.create_text(300, 85, text = "= v x t",font = LABELFONT, fill="red")
1567 self.velocityCanvas.create_text(300, 115, text = "Displacement",font = LABELFONT, fill="red")
1568
1569 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1570 self.accelerationCanvas.create_line(50, 135, 50, 35, width = 3, fill="red")
1571 self.accelerationCanvas.create_line(50, 135, 200, 135, width = 3, fill="red")
1572 self.accelerationCanvas.create_line(50, 135, 195, 40, fill="red")
1573 self.accelerationCanvas.create_text(25, 85, text = "a",font = LABELFONT, fill="red")
1574 self.accelerationCanvas.create_text(125, 153, text = "t",font = LABELFONT, fill="red")
1575 self.accelerationCanvas.create_text(300, 55, text = "Area Under Curve",font = LABELFONT, fill="red")
1576 self.accelerationCanvas.create_text(300, 85, text = "= a x t",font = LABELFONT, fill="red")
1577 self.accelerationCanvas.create_text(300, 115, text = "Velocity",font = LABELFONT, fill="red")
1578
1579 def returnTimeGraphs():
1580 window.addScore(2)
1581 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1582 controller.show_frame(timeGraphsPage)
1583
1584class BouncingBallPage(tk.Frame):
1585
1586 def __init__(self, parent, controller):
1587
1588 tk.Frame.__init__(self, parent)
1589 self.title = tk.Label(self, text="Bouncing Ball Example Page", font=LARGEFONT)
1590 self.title.grid(row=0, column=0, columnspan=2, padx=10, pady=10)
1591
1592 self.bouncingBallCanvas = Canvas(self, width=400, height=160, bd=2)
1593 self.bouncingBallCanvas.grid(row=1, column=0)
1594 self.distanceCanvas = Canvas(self, width=400, height=160, bd=2)
1595 self.distanceCanvas.grid(row=1, column=1)
1596 self.displacementCanvas = Canvas(self, width=400, height=160, bd=2)
1597 self.displacementCanvas.grid(row=2, column=0)
1598 self.speedCanvas = Canvas(self, width=400, height=160, bd=2)
1599 self.speedCanvas.grid(row=2, column=1)
1600 self.velocityCanvas = Canvas(self, width=400, height=160, bd=2)
1601 self.velocityCanvas.grid(row=3, column=0)
1602 self.accelerationCanvas = Canvas(self, width=400, height=160, bd=2)
1603 self.accelerationCanvas.grid(row=3, column=1)
1604
1605 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1606 self.returnButton.grid(row=4, column=0, ipadx=10)
1607
1608 self.scalarVectorLabel = tk.Label(self, text = "Blue = Scalar , Red = Vector")
1609 self.scalarVectorLabel.grid(row=4, column=1, padx=5)
1610
1611 self.bouncingBallCanvas.create_oval(50, 10, 90, 50, fill="gray40", outline="gray40")
1612 self.bouncingBallCanvas.create_oval(50, 150, 90, 110, fill="gray40", outline="gray40")
1613 self.bouncingBallCanvas.create_line(60, 50, 60, 110, width=2)
1614 self.bouncingBallCanvas.create_line(80, 50, 80, 110, width=2)
1615 self.bouncingBallCanvas.create_line(60, 50, 45, 65, width=2)
1616 self.bouncingBallCanvas.create_line(80, 110, 95, 95, width=2)
1617 self.bouncingBallCanvas.create_line(20, 152, 130, 152, width = 3)
1618 self.bouncingBallCanvas.create_text(265, 30, text = "Common Exam Question", font = MEDIUMFONT)
1619 self.bouncingBallCanvas.create_text(265, 70, text = "Ball projected upward, whilst", font = SMALLFONT)
1620 self.bouncingBallCanvas.create_text(265, 100, text = "in the air, it is acted on", font = SMALLFONT)
1621 self.bouncingBallCanvas.create_text(265, 130, text = "by gravity only", font = SMALLFONT)
1622
1623 self.distanceCanvas.create_text(200, 15, text = "Distance - Time Graph", font = MEDIUMFONT, fill="blue")
1624 self.distanceCanvas.create_line(125, 135, 125, 35, width = 3, fill="blue")
1625 self.distanceCanvas.create_line(125, 135, 275, 135, width = 3, fill="blue")
1626 self.distanceCanvas.create_arc(127, 85, 275, 185, start=90, extent=90, style="arc", outline="blue")
1627 self.distanceCanvas.create_arc(127, -15, 275, 85, start=-90, extent=90, style="arc", outline="blue")
1628 self.distanceCanvas.create_text(100, 85, text = "d",font = LABELFONT, fill="blue")
1629 self.distanceCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="blue")
1630
1631 self.displacementCanvas.create_text(200, 15, text = "Displacement - Time Graph",font = MEDIUMFONT, fill="red")
1632 self.displacementCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1633 self.displacementCanvas.create_line(125, 135, 275, 135, width = 3, fill="red")
1634 self.displacementCanvas.create_arc(127, 35, 275, 235, extent=180, style="arc", outline="red")
1635 self.displacementCanvas.create_text(100, 85, text = "s",font = LABELFONT, fill="red")
1636 self.displacementCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="red")
1637
1638 self.speedCanvas.create_text(200, 15, text = "Speed - Time Graph",font = MEDIUMFONT, fill="blue")
1639 self.speedCanvas.create_line(125, 135, 125, 35, width = 3, fill="blue")
1640 self.speedCanvas.create_line(125, 135, 275, 135, width = 3, fill="blue")
1641 self.speedCanvas.create_arc(127, -60, 275, 133, extent=-180, style="arc", outline="blue")
1642 self.speedCanvas.create_text(100, 85, text = "s",font = LABELFONT, fill="blue")
1643 self.speedCanvas.create_text(200, 153, text = "t",font = LABELFONT, fill="blue")
1644
1645 self.velocityCanvas.create_text(200, 15, text = "Velocity - Time Graph",font = MEDIUMFONT, fill="red")
1646 self.velocityCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1647 self.velocityCanvas.create_line(125, 85, 275, 85, width = 3, fill="red")
1648 self.velocityCanvas.create_line(125, 35, 275, 135, fill="red")
1649 self.velocityCanvas.create_text(100, 85, text = "v",font = LABELFONT, fill="red")
1650 self.velocityCanvas.create_text(295, 85, text = "t",font = LABELFONT, fill="red")
1651
1652 self.accelerationCanvas.create_text(200, 15, text = "Acceleration - Time Graph",font = MEDIUMFONT, fill="red")
1653 self.accelerationCanvas.create_line(125, 135, 125, 35, width = 3, fill="red")
1654 self.accelerationCanvas.create_line(125, 85, 275, 85, width = 3, fill="red")
1655 self.accelerationCanvas.create_line(125, 50, 275, 50, fill="red")
1656 self.accelerationCanvas.create_text(100, 85, text = "a",font = LABELFONT, fill="red")
1657 self.accelerationCanvas.create_text(295, 85, text = "t",font = LABELFONT, fill="red")
1658 self.accelerationCanvas.create_text(329, 49, text = "= 9.8 ms^-1",font = SMALLFONT, fill="red")
1659
1660 def returnTimeGraphs():
1661 window.addScore(2)
1662 messagebox.showinfo("Score Updated","You earned 2 points for visiting this page", icon = "info")
1663 controller.show_frame(timeGraphsPage)
1664
1665class interactiveGraphsPage(tk.Frame):
1666
1667 def __init__(self, parent, controller):
1668
1669 self.interactiveGraphsPage = True
1670
1671 tk.Frame.__init__(self, parent)
1672 self.title = tk.Label(self, text="Interactive Graphs Page", font=LARGEFONT)
1673 self.title.pack(side="top", padx=10, pady=10)
1674
1675 # Draws the backend graph onto the tkinter page
1676 canvas = FigureCanvasTkAgg(f, self)
1677 canvas.show()
1678 canvas.get_tk_widget().pack(side="top")
1679
1680 # Draws the matplotlib's navigation tool onto the canvas
1681 toolbar = NavigationToolbar2TkAgg(canvas, self)
1682 toolbar.update()
1683 canvas._tkcanvas.pack(side="top")
1684
1685 # Creates the graph controller window
1686 self.graphControllerDisplayButton = tk.Button(self, text = "Click for Graph Controller", command=lambda: self.createGraphController())
1687 self.graphControllerDisplayButton.pack(side="left", ipadx=10, padx=80, pady=1)
1688
1689 self.returnButton = tk.Button(self, text = "Return - Click to earn points for visiting this revision page", command=lambda: returnTimeGraphs())
1690 self.returnButton.pack(side="left", ipadx=10, padx=61, pady=1)
1691
1692 def returnTimeGraphs():
1693 window.addScore(5)
1694 messagebox.showinfo("Score Updated","You earned 5 points for visiting this page", icon = "info")
1695 controller.show_frame(timeGraphsPage)
1696
1697 def onclick(event): # Called when graph canvas is clicked
1698 try: # If user clicks outside of the graph, it causes an error. If not, call the onClickFunction
1699 onclickFunction(event)
1700 except: # If they do click outside the graph, nothing should happen so just pass
1701 pass
1702
1703 def onclickFunction(event): # Called when graph is clicked
1704 xValue = event.xdata # Retrieves the x coordinate of the user's click
1705
1706 if xValue % 1 != 0: # Checks whether x coordinate has a gradient, if yes....
1707 yValue = event.ydata # Retrieves the y coordinate of the user's click
1708
1709 lowerX = math.floor(xValue) #
1710 upperX = math.ceil(xValue)
1711
1712 try:
1713 lowerPosition = animationState.xList.index(lowerX)
1714 lowerY = animationState.yList[lowerPosition]
1715
1716 upperPosition = animationState.xList.index(upperX)
1717 upperY = animationState.yList[upperPosition]
1718
1719 gradient = (upperY - lowerY)/(upperX - lowerX)
1720
1721 units = animationState.gradientUnits()
1722
1723 messagebox.showinfo("Gradient", "Gradient = {0} {1}".format(gradient, units), icon="info")
1724 except:
1725 messagebox.showinfo("Error", "Not enough data\n\nClick somewhere where a line is present", icon="warning")
1726
1727 else: # Called if user clicks where x coordinate is an integer
1728 messagebox.showinfo("Error", "You have clicked where the line changes gradient\n\nTry again", icon="warning")
1729
1730
1731 f.canvas.callbacks.connect("button_press_event", onclick) # When the user clicks on the graph canvas, call the onlick function
1732
1733 def createGraphController(self): # Creates the graph controller window, if its already made, focus on the window
1734 try:
1735 self.graphController.lift()
1736 self.graphController.focus_force()
1737 except:
1738 self.graphController = GraphController()
1739 self.graphController.protocol("WM_DELETE_WINDOW", self.closeGraphController)
1740 self.graphController.mainloop()
1741
1742 def closeGraphController(self): # Pauses the backend of the graph, and destroys the graph controller window
1743 animationState.play = False
1744 self.graphController.destroy()
1745
1746class GraphController(tk.Tk):
1747
1748 def __init__(self, *args, **kwargs):
1749
1750 self.present = True
1751
1752 tk.Tk.__init__(self, *args, **kwargs)
1753
1754 tk.Tk.iconbitmap(self, default="NEALogo.ico")
1755 tk.Tk.wm_title(self, "Graph Controller")
1756
1757 self.geometry("480x350")
1758 self.resizable(width=False,height=False)
1759
1760 self.currentState = tk.Label(self, text = "Graph is: Paused")
1761 self.currentState.grid(row=0, column=0, padx=30, pady=20)
1762
1763 self.playPauseButton = tk.Button(self, text="Play/Pause", command=lambda: self.playPause())
1764 self.playPauseButton.grid(row=0, column=1, padx=30, pady=20)
1765
1766 self.line1 = tk.Label(self, text = "---------------------------------------------------------")
1767 self.line1.grid(row=1, column=0, columnspan=2)
1768
1769 self.selectGraphType = tk.Label(self, text = "Select Graph Type: (Default is Distance-Time Graph)")
1770 self.selectGraphType.grid(row=2, column=0, columnspan=2, padx=30, pady=15)
1771
1772 self.distanceButton = tk.Button(self, text = "Distance-Time Graph", command=lambda: self.changeLabels("distance /m"))
1773 self.distanceButton.grid(row=3, column=0, padx=10, pady=10)
1774
1775 self.displacementButton = tk.Button(self, text = "Displacement-Time Graph", command=lambda: self.changeLabels("displacement /m"))
1776 self.displacementButton.grid(row=3, column=1, padx=10, pady=10)
1777
1778 self.speedButton = tk.Button(self, text = "Speed-Time Graph", command=lambda: self.changeLabels("speed /ms^-1"))
1779 self.speedButton.grid(row=4, column=0, padx=10, pady=10)
1780
1781 self.velocityButton = tk.Button(self, text = "Velocity-Time Graph", command=lambda: self.changeLabels("velocity /ms^-1"))
1782 self.velocityButton.grid(row=4, column=1, padx=10, pady=10)
1783
1784 self.accelerationButton = tk.Button(self, text = "Acceleration-Time Graph", command=lambda: self.changeLabels("acceleration /ms^-2"))
1785 self.accelerationButton.grid(row=5, column=0, columnspan=2, padx=10, pady=10)
1786
1787 self.line2 = tk.Label(self, text = "---------------------------------------------------------")
1788 self.line2.grid(row=6, column=0, columnspan=2)
1789
1790 self.areaUnderGraphButton = tk.Button(self, text = "Area Under Graph", command=lambda: self.areaUnderGraph())
1791 self.areaUnderGraphButton.grid(row=7, column=0, padx=10, pady=10)
1792
1793 self.clearGraphButton = tk.Button(self, text = "Clear Graph", command=lambda: self.clearGraph())
1794 self.clearGraphButton.grid(row=7, column=1, padx=10, pady=10)
1795
1796 self.line3 = tk.Label(self, text = """|
1797|
1798|
1799|
1800|
1801|
1802|
1803|
1804|
1805|
1806|
1807|
1808|
1809|
1810|
1811|
1812|
1813|
1814|
1815|
1816|""")
1817 self.line3.grid(row=0, column=2, rowspan=8, padx=5)
1818
1819 self.gradientLabel = tk.Label(self, text = "Slide to change\ngraph gradient")
1820 self.gradientLabel.grid(row=0, column=3, sticky=N, padx=13, pady=15)
1821
1822 self.gradientScale = tk.Scale(self, from_=10, to=-10, length=250)
1823 self.gradientScale.grid(row=1, column=3, rowspan=7, sticky=W, padx=25)
1824
1825 self.scaleLabel = tk.Label(self, text = "- 0")
1826 self.scaleLabel.place(x=423, y=193)
1827
1828 def playPause(self): # Pauses or runs the backend of the graph
1829 animationState.changeState()
1830 if animationState.play == True:
1831 self.currentState.configure(text="Graph is: Running") # Changes the label on the graph controller as well
1832 elif animationState.play == False:
1833 self.currentState.configure(text="Graph is: Paused")
1834
1835 def changeLabels(self, graphType): # Alters the graph's axis labels to the appropriate graph
1836 self.gradientScale.set(0)
1837 animationState.play = False
1838 a.clear()
1839 animationState.xList = [0]
1840 animationState.yList = [0]
1841 animationState.xCounter = 1
1842 titlePart = graphType.capitalize()
1843 titlePart2 = titlePart.split(" ")[0]
1844 title = "{0}-Time Graph".format(titlePart2)
1845 animationState.title = title
1846 animationState.ylabel = graphType
1847 animationState.xlabel = "time /s"
1848
1849 def changeGradient(self, changeAmount):
1850 animationState.currentGradient += changeAmount
1851
1852 def areaUnderGraph(self):
1853 animationState.play = False
1854 area = areaUnderCurve(animationState.xList, animationState.yList)
1855 if area == "N/A": # Returned from areaUnderCurve function, means not enough data was given
1856 pass
1857 else:
1858 units = animationState.areaUnits()
1859 messagebox.showinfo("Area Under Curve", "Area Under Curve = {0} {1}".format(area, units), icon="info")
1860
1861 def clearGraph(self): # Clears the graph, resets the lists of coordinates and resets the xCounter
1862 animationState.play = False
1863 self.gradientScale.set(0)
1864 a.clear()
1865 animationState.xList = [0]
1866 animationState.yList = [0]
1867 animationState.xCounter = 1
1868
1869# SUVAT Revision Pages #
1870
1871class SUVATRevisionPage(tk.Frame):
1872
1873 def __init__(self, parent, controller):
1874
1875 tk.Frame.__init__(self, parent)
1876 self.title = tk.Label(self, text="SUVAT Revision Page", font=LARGEFONT)
1877 self.title.pack(side="top", fill="x", pady=30)
1878
1879 self.SUVATCheckerButton = tk.Button(self, text = "SUVAT Checker", command=lambda: controller.show_frame(SUVATCheckerPage),height=2,width=20)
1880 self.label1 = tk.Label(self, text = "or ")
1881 self.SUVATSolverButton = tk.Button(self, text = "SUVAT Solver", command=lambda: controller.show_frame(SUVATSolverPage),height=2,width=20)
1882
1883 self.SUVATCheckerButton.pack(side="top", pady=5)
1884 self.label1.pack(side="top", fill="x", pady=5)
1885 self.SUVATSolverButton.pack(side="top", pady=5)
1886
1887class SUVATCheckerPage(tk.Frame):
1888
1889 def __init__(self, parent, controller):
1890
1891 tk.Frame.__init__(self, parent)
1892 self.title = tk.Label(self, text="SUVAT Checker Page", font=LARGEFONT)
1893 self.title.pack(padx=10, pady=10)
1894
1895 # Instructions explaining to the user what to enter into each entry field
1896 self.instructions = tk.Label(self, text = """You should have 3 known values, and an unknown value that you want to find
1897For each of the boxes below, either:
18981. The value is known so type it into the box
18992. The value is unknown and the value you want to find, type in 'A'
19003. The value is unknown and not the value you want to find, type in 'X'
1901
1902Enter your answer in the box by the check button (Please enter your answer to 2 decimal places. No units!)
1903A correct answer will earn you 10 points
1904An incorrect answer will lose you 1 point
1905""")
1906
1907 self.displacementText = tk.Label(self, text = "Displacement:")
1908 self.displacementInput = tk.Entry(self, justify="center", width=30)
1909
1910 self.initialVelocityText = tk.Label(self, text = "Initial Velocity:")
1911 self.initialVelocityInput = tk.Entry(self, justify="center", width=30)
1912
1913 self.finalVelocityText = tk.Label(self, text = "Final Velocity:")
1914 self.finalVelocityInput = tk.Entry(self, justify="center", width=30)
1915
1916 self.accelerationText = tk.Label(self, text = "Acceleration:")
1917 self.accelerationInput = tk.Entry(self, justify="center", width=30)
1918
1919 self.timeTakenText = tk.Label(self, text = "Time Taken:")
1920 self.timeTakenInput = tk.Entry(self, justify="center", width=30)
1921
1922 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!")
1923 self.userAnswerInput = tk.Entry(self, justify="center", width=30)
1924 self.submitButton = tk.Button(self,text = "Check", command=lambda: Solve())
1925
1926 self.instructions.pack(side="top", fill="x")
1927
1928 self.displacementText.pack(side="top", fill="x", padx=20, pady=5)
1929 self.displacementInput.pack(side="top")
1930 self.displacementInput.focus()
1931
1932 self.initialVelocityText.pack(side="top", fill="x", padx=20, pady=5)
1933 self.initialVelocityInput.pack(side="top")
1934
1935 self.finalVelocityText.pack(side="top", fill="x", padx=20, pady=5)
1936 self.finalVelocityInput.pack(side="top")
1937
1938 self.accelerationText.pack(side="top", fill="x", padx=20, pady=5)
1939 self.accelerationInput.pack(side="top")
1940
1941 self.timeTakenText.pack(side="top", fill="x", padx=20, pady=5)
1942 self.timeTakenInput.pack(side="top")
1943
1944 self.userAnswerText.pack(side="top", fill="x", padx=20, pady=5)
1945 self.userAnswerInput.pack(side="top", pady=5)
1946 self.submitButton.pack(side="bottom", fill="y", padx=20, pady=5)
1947
1948 def Solve():
1949
1950 def deleteEntryFields(): # Clears all the entry fields
1951 self.displacementInput.delete(0,"end")
1952 self.initialVelocityInput.delete(0,"end")
1953 self.finalVelocityInput.delete(0,"end")
1954 self.accelerationInput.delete(0,"end")
1955 self.timeTakenInput.delete(0,"end")
1956 self.userAnswerInput.delete(0,"end")
1957
1958 def response(message1, message2, score, add): # Displays answer to user
1959 messagebox.showinfo("{0}".format(message1),"{0}".format(message2), icon = "info")
1960 if add:
1961 window.addScore(score)
1962 messagebox.showinfo("Score Updated","You earnt {0} points for beating PHYSICS".format(score), icon = "info")
1963 else:
1964 window.addScore(-score)
1965 messagebox.showinfo("Score Updated","You lost {0} points for losing to PHYSICS".format(score), icon = "info")
1966
1967 try:
1968 s = self.displacementInput.get().lower()
1969 except:
1970 s = self.displacementInput.get()
1971 try:
1972 u = self.initialVelocityInput.get().lower()
1973 except:
1974 u = self.initialVelocityInput.get()
1975 try:
1976 v = self.finalVelocityInput.get().lower()
1977 except:
1978 v = self.finalVelocityInput.get()
1979 try:
1980 a = self.accelerationInput.get().lower()
1981 except:
1982 a = self.accelerationInput.get()
1983 try:
1984 t = self.timeTakenInput.get().lower()
1985 except:
1986 t = self.timeTakenInput.get()
1987
1988 suvat = [s,u,v,a,t]
1989
1990 Xcount = 0
1991 Acount = 0
1992 numberCount = 0
1993 for each in suvat:
1994 if each.lower() == "x": # Increments if an x is found
1995 Xcount += 1
1996 elif each.lower() == "a":
1997 Acount += 1
1998
1999 if each.isdigit(): # Used to ensure 3 values are numbers and stops the program crashing from attempting to convert letters into float numbers
2000 numberCount += 1
2001 elif "-" in each or "." in each: # If there is a negative or decimal number, still increment the numberCount
2002 numberCount += 1
2003
2004 if "" in suvat:
2005 messagebox.showinfo("Error!","Leave no fields blank", icon="warning")
2006 elif self.userAnswerInput.get() == "":
2007 messagebox.showinfo("Error!","Enter an answer to check", icon="warning")
2008 elif Acount == 0:
2009 messagebox.showinfo("Error!","No value given as 'A'", icon="warning")
2010 elif Acount > 1:
2011 messagebox.showinfo("Error!","Only one value can be given as 'A'", icon="warning")
2012 elif Xcount == 0:
2013 messagebox.showinfo("Error!","No value given as 'X'", icon="warning")
2014 elif Xcount > 1:
2015 messagebox.showinfo("Error!","Only one value can be given as 'X'", icon="warning")
2016 elif numberCount != 3:
2017 messagebox.showinfo("Error!","Three values entered must be numbers", icon="warning")
2018 else:
2019 Solver = SUVAT(s,u,v,a,t,shortAnswer=True) # Calls the Solving algorithm, passing through the parameters given by the user
2020 # shortAnswer is True, so only the value is returned, no text
2021 answer = Solver.initiate() # Returns the answer(s) from the SUVAT equations
2022
2023 if answer == "None": # If an error occurs in the mathematics, 'None' is returned and this stops an error occuring
2024 messagebox.showinfo("Error","Invalid details inputted, try again", icon = "warning")
2025
2026 else:
2027 if answer == "zeroDivision error":
2028 messagebox.showinfo("Error","Invalid details inputted, resulting in a calculation involving a division by 0\n\nCheck your numbers", icon = "warning")
2029
2030 else:
2031 userAnswer = self.userAnswerInput.get()
2032
2033 if "," in userAnswer: # Comma suggests the answer has two parts, and so continues onto code that compares two answers
2034 userAnswer1, userAnswer2 = userAnswer.split(",") # Splits the answers into two variables
2035 answer1, answer2 = "{:.2f}".format(answer[0]), "{:.2f}".format(answer[1]) # Converts answers from SUVAT() into 2 decimal place variables
2036 userAnswer1, userAnswer2 = "{:.2f}".format(float(userAnswer1)), "{:.2f}".format(float(userAnswer2))
2037
2038 if userAnswer1 == answer1:
2039 if userAnswer2 == answer2:
2040 response("Correct!!", "You got both answers correct", 15, True)
2041 deleteEntryFields()
2042
2043 else: # First correct, second incorrect
2044 response("Incorrect!!", "Your second answer was wrong, try again", 1, False)
2045 self.userAnswerInput.delete(",","end")
2046
2047 else: # First answer is incorrect
2048 if userAnswer2 == answer2: #
2049 response("Incorrect!!", "Your first answer was wrong, try again", 1, False)
2050 self.userAnswerInput.delete(0,",")
2051
2052 else:
2053 if userAnswer1 == answer2:# If user enters dual answers in wrong order, compare opposite answers
2054 if userAnswer2 == answer1:
2055 response("Correct!!", "You got both answers correct", 15, True)
2056 deleteEntryFields()
2057
2058 else: # First answer correct, second incorrect
2059 response("Incorrect!!", "Your second answer was wrong, try again", 1, False)
2060 self.userAnswerInput.delete(0,",")
2061
2062 elif userAnswer2 == answer1:
2063 response("Incorrect!!", "Your first answer was wrong, try again", 1, False)
2064 self.userAnswerInput.delete(",","end")
2065
2066 else:
2067 response("Incorrect!!", "You got both answers wrong, try again", 2, False)
2068 self.userAnswerInput.delete(0,"end")
2069
2070 else: # No comma so only one part, so only us the first element in array 'answer'
2071 answer = "{0:.2f}".format(answer[0])
2072 if userAnswer == answer:
2073 response("Correct!!", "You got the right answer!!", 10, True)
2074 deleteEntryFields()
2075
2076 else:
2077 response("Incorrect!!", "You entered the wrong answer", 1, False)
2078 self.userAnswerInput.delete(0,"end")
2079
2080class SUVATSolverPage(tk.Frame):
2081
2082 def __init__(self, parent, controller):
2083
2084 tk.Frame.__init__(self, parent)
2085 self.title = tk.Label(self, text="SUVAT Solver Page", font=LARGEFONT)
2086 self.title.pack(padx=10, pady=10)
2087
2088 self.instructions = tk.Label(self, text = """You should have 3 known values, and an unknown value that you want to find
2089For each of the boxes below, either:
20901. The value is known so type it into the box
20912. The value is unknown and the value you want to find, type in 'A'
20923. The value is unknown and not the value you want to find, type in 'X'
2093
2094Warning!!! Every answer you seek from this tool with remove 5 points from your account!
2095""")
2096
2097 self.displacementText = tk.Label(self, text = "Displacement:")
2098 self.displacementInput = tk.Entry(self, justify="center", width=30)
2099
2100 self.initialVelocityText = tk.Label(self, text = "Initial Velocity:")
2101 self.initialVelocityInput = tk.Entry(self, justify="center", width=30)
2102
2103 self.finalVelocityText = tk.Label(self, text = "Final Velocity:")
2104 self.finalVelocityInput = tk.Entry(self, justify="center", width=30)
2105
2106 self.accelerationText = tk.Label(self, text = "Acceleration:")
2107 self.accelerationInput = tk.Entry(self, justify="center", width=30)
2108
2109 self.timeTakenText = tk.Label(self, text = "Time Taken:")
2110 self.timeTakenInput = tk.Entry(self, justify="center", width=30)
2111
2112 self.submitButton = tk.Button(self,text = "Solve", command=lambda: Solve())
2113
2114 self.instructions.pack(side="top", fill="x", pady=15)
2115
2116 self.displacementText.pack(side="top", fill="x", padx=20, pady=5)
2117 self.displacementInput.pack(side="top")
2118 self.displacementInput.focus()
2119
2120 self.initialVelocityText.pack(side="top", fill="x", padx=20, pady=5)
2121 self.initialVelocityInput.pack(side="top")
2122
2123 self.finalVelocityText.pack(side="top", fill="x", padx=20, pady=5)
2124 self.finalVelocityInput.pack(side="top")
2125
2126 self.accelerationText.pack(side="top", fill="x", padx=20, pady=5)
2127 self.accelerationInput.pack(side="top")
2128
2129 self.timeTakenText.pack(side="top", fill="x", padx=20, pady=5)
2130 self.timeTakenInput.pack(side="top")
2131
2132 self.submitButton.pack(side="top", fill="y", padx=20, pady=10)
2133
2134 def Solve():
2135
2136 # Validation Statements
2137
2138 try:
2139 s = self.displacementInput.get().lower()
2140 except:
2141 s = self.displacementInput.get()
2142 try:
2143 u = self.initialVelocityInput.get().lower()
2144 except:
2145 u = self.initialVelocityInput.get()
2146 try:
2147 v = self.finalVelocityInput.get().lower()
2148 except:
2149 v = self.finalVelocityInput.get()
2150 try:
2151 a = self.accelerationInput.get().lower()
2152 except:
2153 a = self.accelerationInput.get()
2154 try:
2155 t = self.timeTakenInput.get().lower()
2156 except:
2157 t = self.timeTakenInput.get()
2158
2159 suvat = [s,u,v,a,t]
2160
2161 Xcount = 0
2162 Acount = 0
2163 numberCount = 0
2164 for each in suvat:
2165 if each.lower() == "x":
2166 Xcount += 1
2167 elif each.lower() == "a":
2168 Acount += 1
2169
2170 if each.isdigit(): # Used to ensure 3 values are numbers and stops the program crashing from attempting to convert letters into float numbers
2171 numberCount += 1
2172 elif "-" in each or "." in each:
2173 numberCount += 1
2174
2175 if "" in suvat:
2176 messagebox.showinfo("Error!","Leave no fields blank", icon="warning")
2177 elif Acount == 0:
2178 messagebox.showinfo("Error!","No value given as 'A'", icon="warning")
2179 elif Acount > 1:
2180 messagebox.showinfo("Error!","Only one value can be given as 'A'", icon="warning")
2181 elif Xcount == 0:
2182 messagebox.showinfo("Error!","No value given as 'X'", icon="warning")
2183 elif Xcount > 1:
2184 messagebox.showinfo("Error!","Only one value can be given as 'X'", icon="warning")
2185 elif numberCount != 3:
2186 messagebox.showinfo("Error!","Three values entered must be numbers", icon="warning")
2187 else:
2188 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
2189 answer = Solver.initiate()
2190 if answer == "None":
2191 messagebox.showinfo("Error","Invalid values inputted, try again", icon = "warning")
2192
2193 else:
2194 if answer == "zeroDivision error":
2195 messagebox.showinfo("Error","Invalid details inputted, resulting in a calculation involving a division by 0\n\nCheck your numbers", icon = "warning")
2196
2197 else:
2198 messagebox.showinfo("Answer","{0}".format(answer), icon = "info")
2199 window.addScore(-5)
2200 messagebox.showinfo("Score Updated","You lost 5 points for cheating PHYSICS by just wanting an answer", icon = "info")
2201 deleteEntryFields()
2202
2203
2204 def deleteEntryFields():
2205 self.displacementInput.delete(0,"end")
2206 self.initialVelocityInput.delete(0,"end")
2207 self.finalVelocityInput.delete(0,"end")
2208 self.accelerationInput.delete(0,"end")
2209 self.timeTakenInput.delete(0,"end")
2210
2211#############################
2212
2213
2214
2215
2216
2217##### Tkinter Runtime #####
2218
2219def exitProgram():
2220 if messagebox.askokcancel("Quit", "Do you wish to quit?"):
2221 window.logout()
2222 try: # If the graph controller is still open, destroy it
2223 window.frames[interactiveGraphsPage].graphController.destroy()
2224 except:
2225 pass
2226 window.destroy()
2227
2228
2229if runProgram: # If any python modules are missing, the Tkinter window will not be called and the program will cease
2230 window = GUI()
2231 ani = animation.FuncAnimation(f,animate, interval=500)
2232 window.protocol("WM_DELETE_WINDOW", exitProgram) # If 'x' in top right corner of GUI window pressed, call exitProgram()
2233 window.mainloop()
2234
2235###########################