Advertisement
Carmarco

NEA Code

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