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